diff options
Diffstat (limited to 'chart2/source/view/charttypes')
18 files changed, 7385 insertions, 0 deletions
diff --git a/chart2/source/view/charttypes/AreaChart.cxx b/chart2/source/view/charttypes/AreaChart.cxx new file mode 100644 index 000000000000..4d2f5ad8716b --- /dev/null +++ b/chart2/source/view/charttypes/AreaChart.cxx @@ -0,0 +1,981 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "AreaChart.hxx" +#include "PlottingPositionHelper.hxx" +#include "ShapeFactory.hxx" +#include "CommonConverters.hxx" +#include "macros.hxx" +#include "ViewDefines.hxx" +#include "ObjectIdentifier.hxx" +#include "Splines.hxx" +#include "ChartTypeHelper.hxx" +#include "LabelPositionHelper.hxx" +#include "Clipping.hxx" +#include "Stripe.hxx" +#include "PolarLabelPositionHelper.hxx" + +#include <com/sun/star/chart2/Symbol.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart/MissingValueTreatment.hpp> +#include <tools/debug.hxx> +#include <editeng/unoprnms.hxx> +#include <rtl/math.hxx> +#include <com/sun/star/drawing/DoubleSequence.hpp> +#include <com/sun/star/drawing/NormalsKind.hpp> +#include <com/sun/star/lang/XServiceName.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::rtl::math; +using namespace ::com::sun::star::chart2; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +AreaChart::AreaChart( const uno::Reference<XChartType>& xChartTypeModel + , sal_Int32 nDimensionCount + , bool bCategoryXAxis + , bool bNoArea + , PlottingPositionHelper* pPlottingPositionHelper + , bool bConnectLastToFirstPoint + , bool bExpandIfValuesCloseToBorder + , sal_Int32 nKeepAspectRatio + , const drawing::Direction3D& rAspectRatio + ) + : VSeriesPlotter( xChartTypeModel, nDimensionCount, bCategoryXAxis ) + , m_pMainPosHelper(pPlottingPositionHelper) + , m_bArea(!bNoArea) + , m_bLine(bNoArea) + , m_bSymbol( ChartTypeHelper::isSupportingSymbolProperties(xChartTypeModel,nDimensionCount) ) + , m_bIsPolarCooSys( bConnectLastToFirstPoint ) + , m_bConnectLastToFirstPoint( bConnectLastToFirstPoint ) + , m_bExpandIfValuesCloseToBorder( bExpandIfValuesCloseToBorder ) + , m_nKeepAspectRatio(nKeepAspectRatio) + , m_aGivenAspectRatio(rAspectRatio) + , m_eCurveStyle(CurveStyle_LINES) + , m_nCurveResolution(20) + , m_nSplineOrder(3) + , m_xSeriesTarget(0) + , m_xErrorBarTarget(0) + , m_xTextTarget(0) + , m_xRegressionCurveEquationTarget(0) +{ + if( !m_pMainPosHelper ) + m_pMainPosHelper = new PlottingPositionHelper(); + PlotterBase::m_pPosHelper = m_pMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper; + + try + { + if( m_xChartTypeModelProps.is() ) + { + m_xChartTypeModelProps->getPropertyValue( C2U( "CurveStyle" ) ) >>= m_eCurveStyle; + m_xChartTypeModelProps->getPropertyValue( C2U( "CurveResolution" ) ) >>= m_nCurveResolution; + m_xChartTypeModelProps->getPropertyValue( C2U( "SplineOrder" ) ) >>= m_nSplineOrder; + } + } + catch( uno::Exception& e ) + { + //the above properties are not supported by all charttypes supported by this class (e.g. area or net chart) + //in that cases this exception is ok + e.Context.is();//to have debug information without compilation warnings + } +} + +AreaChart::~AreaChart() +{ + delete m_pMainPosHelper; +} + +double AreaChart::getMinimumX() +{ + if( m_bCategoryXAxis && m_bIsPolarCooSys )//the angle axis in net charts needs a different autoscaling + return 1.0;//first category (index 0) matches with real number 1.0 + return VSeriesPlotter::getMinimumX(); +} + +double AreaChart::getMaximumX() +{ + if( m_bCategoryXAxis && m_bIsPolarCooSys )//the angle axis in net charts needs a different autoscaling + return getPointCount()+1; + return VSeriesPlotter::getMaximumX(); +} + +bool AreaChart::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex ) +{ + return m_bExpandIfValuesCloseToBorder && + VSeriesPlotter::isExpandIfValuesCloseToBorder( nDimensionIndex ); +} + +bool AreaChart::isSeperateStackingForDifferentSigns( sal_Int32 /*nDimensionIndex*/ ) +{ + // no separate stacking in all types of line/area charts + return false; +} + +//----------------------------------------------------------------- + +LegendSymbolStyle AreaChart::getLegendSymbolStyle() +{ + if( m_bArea || m_nDimension == 3 ) + return chart2::LegendSymbolStyle_BOX; + return chart2::LegendSymbolStyle_LINE_WITH_SYMBOL; +} + +uno::Any AreaChart::getExplicitSymbol( const VDataSeries& rSeries, sal_Int32 nPointIndex ) +{ + uno::Any aRet; + + Symbol* pSymbolProperties = rSeries.getSymbolProperties( nPointIndex ); + if( pSymbolProperties ) + { + aRet = uno::makeAny(*pSymbolProperties); + } + + return aRet; +} + +drawing::Direction3D AreaChart::getPreferredDiagramAspectRatio() const +{ + if( m_nKeepAspectRatio == 1 ) + return m_aGivenAspectRatio; + drawing::Direction3D aRet(1,-1,1); + if( m_nDimension == 2 ) + aRet = drawing::Direction3D(-1,-1,-1); + else + { + drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() ); + aRet.DirectionZ = aScale.DirectionZ*0.2; + if(aRet.DirectionZ>1.0) + aRet.DirectionZ=1.0; + if(aRet.DirectionZ>10) + aRet.DirectionZ=10; + } + return aRet; +} + +bool AreaChart::keepAspectRatio() const +{ + if( m_nKeepAspectRatio == 0 ) + return false; + if( m_nKeepAspectRatio == 1 ) + return true; + if( m_nDimension == 2 ) + { + if( !m_bSymbol ) + return false; + } + return true; +} + +void AreaChart::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot ) +{ + if( m_bArea && !m_bIsPolarCooSys && pSeries ) + { + sal_Int32 nMissingValueTreatment = pSeries->getMissingValueTreatment(); + if( nMissingValueTreatment == ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP ) + pSeries->setMissingValueTreatment( ::com::sun::star::chart::MissingValueTreatment::USE_ZERO ); + } + if( m_nDimension == 3 && !m_bCategoryXAxis ) + { + //3D xy always deep + DBG_ASSERT( zSlot==-1,"3D xy charts should be deep stacked in model also" ); + zSlot=-1; + xSlot=0; + ySlot=0; + } + VSeriesPlotter::addSeries( pSeries, zSlot, xSlot, ySlot ); +} + +void lcl_removeDuplicatePoints( drawing::PolyPolygonShape3D& rPolyPoly, PlottingPositionHelper& rPosHelper ) +{ + sal_Int32 nPolyCount = rPolyPoly.SequenceX.getLength(); + if(!nPolyCount) + return; + + drawing::PolyPolygonShape3D aTmp; + aTmp.SequenceX.realloc(nPolyCount); + aTmp.SequenceY.realloc(nPolyCount); + aTmp.SequenceZ.realloc(nPolyCount); + + for( sal_Int32 nPolygonIndex = 0; nPolygonIndex<nPolyCount; nPolygonIndex++ ) + { + drawing::DoubleSequence* pOuterSourceX = &rPolyPoly.SequenceX.getArray()[nPolygonIndex]; + drawing::DoubleSequence* pOuterSourceY = &rPolyPoly.SequenceY.getArray()[nPolygonIndex]; + drawing::DoubleSequence* pOuterSourceZ = &rPolyPoly.SequenceZ.getArray()[nPolygonIndex]; + + drawing::DoubleSequence* pOuterTargetX = &aTmp.SequenceX.getArray()[nPolygonIndex]; + drawing::DoubleSequence* pOuterTargetY = &aTmp.SequenceY.getArray()[nPolygonIndex]; + drawing::DoubleSequence* pOuterTargetZ = &aTmp.SequenceZ.getArray()[nPolygonIndex]; + + sal_Int32 nPointCount = pOuterSourceX->getLength(); + if( !nPointCount ) + continue; + + pOuterTargetX->realloc(nPointCount); + pOuterTargetY->realloc(nPointCount); + pOuterTargetZ->realloc(nPointCount); + + double* pSourceX = pOuterSourceX->getArray(); + double* pSourceY = pOuterSourceY->getArray(); + double* pSourceZ = pOuterSourceZ->getArray(); + + double* pTargetX = pOuterTargetX->getArray(); + double* pTargetY = pOuterTargetY->getArray(); + double* pTargetZ = pOuterTargetZ->getArray(); + + //copy first point + *pTargetX=*pSourceX++; + *pTargetY=*pSourceY++; + *pTargetZ=*pSourceZ++; + sal_Int32 nTargetPointCount=1; + + for( sal_Int32 nSource=1; nSource<nPointCount; nSource++ ) + { + if( !rPosHelper.isSameForGivenResolution( *pTargetX, *pTargetY, *pTargetZ + , *pSourceX, *pSourceY, *pSourceZ ) ) + { + pTargetX++; pTargetY++; pTargetZ++; + *pTargetX=*pSourceX; + *pTargetY=*pSourceY; + *pTargetZ=*pSourceZ; + nTargetPointCount++; + } + pSourceX++; pSourceY++; pSourceZ++; + } + + //free unused space + if( nTargetPointCount<nPointCount ) + { + pOuterTargetX->realloc(nTargetPointCount); + pOuterTargetY->realloc(nTargetPointCount); + pOuterTargetZ->realloc(nTargetPointCount); + } + + pOuterSourceX->realloc(0); + pOuterSourceY->realloc(0); + pOuterSourceZ->realloc(0); + } + + //free space + rPolyPoly.SequenceX.realloc(nPolyCount); + rPolyPoly.SequenceY.realloc(nPolyCount); + rPolyPoly.SequenceZ.realloc(nPolyCount); + + rPolyPoly=aTmp; +} + +bool AreaChart::impl_createLine( VDataSeries* pSeries + , drawing::PolyPolygonShape3D* pSeriesPoly + , PlottingPositionHelper* pPosHelper ) +{ + //return true if a line was created successfully + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget); + + drawing::PolyPolygonShape3D aPoly; + if(CurveStyle_CUBIC_SPLINES==m_eCurveStyle) + { + drawing::PolyPolygonShape3D aSplinePoly; + SplineCalculater::CalculateCubicSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution ); + lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper ); + Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly ); + } + else if(CurveStyle_B_SPLINES==m_eCurveStyle) + { + drawing::PolyPolygonShape3D aSplinePoly; + SplineCalculater::CalculateBSplines( *pSeriesPoly, aSplinePoly, m_nCurveResolution, m_nSplineOrder ); + lcl_removeDuplicatePoints( aSplinePoly, *pPosHelper ); + Clipping::clipPolygonAtRectangle( aSplinePoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly ); + } + else + { + bool bIsClipped = false; + if( m_bConnectLastToFirstPoint && !ShapeFactory::isPolygonEmptyOrSinglePoint(*pSeriesPoly) ) + { + // do NOT connect last and first point, if one is NAN, and NAN handling is NAN_AS_GAP + double fFirstY = pSeries->getYValue( 0 ); + double fLastY = pSeries->getYValue( VSeriesPlotter::getPointCount() - 1 ); + if( (pSeries->getMissingValueTreatment() != ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP) + || (::rtl::math::isFinite( fFirstY ) && ::rtl::math::isFinite( fLastY )) ) + { + // connect last point in last polygon with first point in first polygon + ::basegfx::B2DRectangle aScaledLogicClipDoubleRect( pPosHelper->getScaledLogicClipDoubleRect() ); + drawing::PolyPolygonShape3D aTmpPoly(*pSeriesPoly); + drawing::Position3D aLast(aScaledLogicClipDoubleRect.getMaxX(),aTmpPoly.SequenceY[0][0],aTmpPoly.SequenceZ[0][0]); + // add connector line to last polygon + AddPointToPoly( aTmpPoly, aLast, pSeriesPoly->SequenceX.getLength() - 1 ); + Clipping::clipPolygonAtRectangle( aTmpPoly, aScaledLogicClipDoubleRect, aPoly ); + bIsClipped = true; + } + } + + if( !bIsClipped ) + Clipping::clipPolygonAtRectangle( *pSeriesPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly ); + } + + if(!ShapeFactory::hasPolygonAnyLines(aPoly)) + return false; + + //transformation 3) -> 4) + pPosHelper->transformScaledLogicToScene( aPoly ); + + //create line: + uno::Reference< drawing::XShape > xShape(NULL); + if(m_nDimension==3) + { + double fDepth = this->getTransformedDepth(); + sal_Int32 nPolyCount = aPoly.SequenceX.getLength(); + for(sal_Int32 nPoly=0;nPoly<nPolyCount;nPoly++) + { + sal_Int32 nPointCount = aPoly.SequenceX[nPoly].getLength(); + for(sal_Int32 nPoint=0;nPoint<nPointCount-1;nPoint++) + { + drawing::Position3D aPoint1, aPoint2; + aPoint1.PositionX = aPoly.SequenceX[nPoly][nPoint+1]; + aPoint1.PositionY = aPoly.SequenceY[nPoly][nPoint+1]; + aPoint1.PositionZ = aPoly.SequenceZ[nPoly][nPoint+1]; + + aPoint2.PositionX = aPoly.SequenceX[nPoly][nPoint]; + aPoint2.PositionY = aPoly.SequenceY[nPoly][nPoint]; + aPoint2.PositionZ = aPoly.SequenceZ[nPoly][nPoint]; + + Stripe aStripe( aPoint1, aPoint2, fDepth ); + + m_pShapeFactory->createStripe(xSeriesGroupShape_Shapes + , Stripe( aPoint1, aPoint2, fDepth ) + , pSeries->getPropertiesOfSeries(), PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), true, 1 ); + } + } + } + else //m_nDimension!=3 + { + xShape = m_pShapeFactory->createLine2D( xSeriesGroupShape_Shapes + , PolyToPointSequence( aPoly ) ); + this->setMappedProperties( xShape + , pSeries->getPropertiesOfSeries() + , PropertyMapper::getPropertyNameMapForLineSeriesProperties() ); + //because of this name this line will be used for marking + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + } + return true; +} + +bool AreaChart::impl_createArea( VDataSeries* pSeries + , drawing::PolyPolygonShape3D* pSeriesPoly + , drawing::PolyPolygonShape3D* pPreviousSeriesPoly + , PlottingPositionHelper* pPosHelper ) +{ + //return true if an area was created successfully + + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeBackChild(pSeries, m_xSeriesTarget); + double zValue = pSeries->m_fLogicZPos; + + drawing::PolyPolygonShape3D aPoly( *pSeriesPoly ); + //add second part to the polygon (grounding points or previous series points) + if( m_bConnectLastToFirstPoint && !ShapeFactory::isPolygonEmptyOrSinglePoint(*pSeriesPoly) ) + { + if( pPreviousSeriesPoly ) + addPolygon( aPoly, *pPreviousSeriesPoly ); + } + else if(!pPreviousSeriesPoly) + { + double fMinX = pSeries->m_fLogicMinX; + double fMaxX = pSeries->m_fLogicMaxX; + double fY = pPosHelper->getBaseValueY();//logic grounding + if( m_nDimension==3 ) + fY = pPosHelper->getLogicMinY(); + + //clip to scale + if(fMaxX<pPosHelper->getLogicMinX() || fMinX>pPosHelper->getLogicMaxX()) + return false;//no visible shape needed + pPosHelper->clipLogicValues( &fMinX, &fY, 0 ); + pPosHelper->clipLogicValues( &fMaxX, 0, 0 ); + + //apply scaling + { + pPosHelper->doLogicScaling( &fMinX, &fY, &zValue ); + pPosHelper->doLogicScaling( &fMaxX, 0, 0 ); + } + + AddPointToPoly( aPoly, drawing::Position3D( fMaxX,fY,zValue) ); + AddPointToPoly( aPoly, drawing::Position3D( fMinX,fY,zValue) ); + } + else + { + appendPoly( aPoly, *pPreviousSeriesPoly ); + } + ShapeFactory::closePolygon(aPoly); + + //apply clipping + { + drawing::PolyPolygonShape3D aClippedPoly; + Clipping::clipPolygonAtRectangle( aPoly, pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly, false ); + ShapeFactory::closePolygon(aClippedPoly); //again necessary after clipping + aPoly = aClippedPoly; + } + + if(!ShapeFactory::hasPolygonAnyLines(aPoly)) + return false; + + //transformation 3) -> 4) + pPosHelper->transformScaledLogicToScene( aPoly ); + + //create area: + uno::Reference< drawing::XShape > xShape(NULL); + if(m_nDimension==3) + { + xShape = m_pShapeFactory->createArea3D( xSeriesGroupShape_Shapes + , aPoly, this->getTransformedDepth() ); + } + else //m_nDimension!=3 + { + xShape = m_pShapeFactory->createArea2D( xSeriesGroupShape_Shapes + , aPoly ); + } + this->setMappedProperties( xShape + , pSeries->getPropertiesOfSeries() + , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + //because of this name this line will be used for marking + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + return true; +} + +void AreaChart::impl_createSeriesShapes() +{ + //the polygon shapes for each series need to be created before + + //iterate through all series again to create the series shapes + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); +//============================================================================= + for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + //============================================================================= + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + //============================================================================= + + std::map< sal_Int32, drawing::PolyPolygonShape3D* > aPreviousSeriesPolyMap;//a PreviousSeriesPoly for each different nAttachedAxisIndex + drawing::PolyPolygonShape3D* pSeriesPoly = NULL; + + //iterate through all series + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + sal_Int32 nAttachedAxisIndex = (*aSeriesIter)->getAttachedAxisIndex(); + PlottingPositionHelper* pPosHelper = &(this->getPlottingPositionHelper( nAttachedAxisIndex )); + if(!pPosHelper) + pPosHelper = m_pMainPosHelper; + PlotterBase::m_pPosHelper = pPosHelper; + + createRegressionCurvesShapes( **aSeriesIter, m_xErrorBarTarget, m_xRegressionCurveEquationTarget, + m_pPosHelper->maySkipPointsInRegressionCalculation()); + + pSeriesPoly = &(*aSeriesIter)->m_aPolyPolygonShape3D; + if( m_bArea ) + { + if( !impl_createArea( *aSeriesIter, pSeriesPoly, aPreviousSeriesPolyMap[nAttachedAxisIndex], pPosHelper ) ) + continue; + } + if( m_bLine ) + { + if( !impl_createLine( *aSeriesIter, pSeriesPoly, pPosHelper ) ) + continue; + } + aPreviousSeriesPolyMap[nAttachedAxisIndex] = pSeriesPoly; + }//next series in x slot (next y slot) + }//next x slot + }//next z slot +} + +namespace +{ + +void lcl_reorderSeries( ::std::vector< ::std::vector< VDataSeriesGroup > >& rZSlots ) +{ + ::std::vector< ::std::vector< VDataSeriesGroup > > aRet( rZSlots.size() ); + + ::std::vector< ::std::vector< VDataSeriesGroup > >::reverse_iterator aZIt( rZSlots.rbegin() ); + ::std::vector< ::std::vector< VDataSeriesGroup > >::reverse_iterator aZEnd( rZSlots.rend() ); + for( ; aZIt != aZEnd; ++aZIt ) + { + ::std::vector< VDataSeriesGroup > aXSlot( aZIt->size() ); + + ::std::vector< VDataSeriesGroup >::reverse_iterator aXIt( aZIt->rbegin() ); + ::std::vector< VDataSeriesGroup >::reverse_iterator aXEnd( aZIt->rend() ); + for( ; aXIt != aXEnd; ++aXIt ) + aXSlot.push_back(*aXIt); + + aRet.push_back(aXSlot); + } + + rZSlots.clear(); + rZSlots = aRet; +} + +}//anonymous namespace + +//better performance for big data +struct FormerPoint +{ + FormerPoint( double fX, double fY, double fZ ) + : m_fX(fX), m_fY(fY), m_fZ(fZ) + {} + FormerPoint() + { + ::rtl::math::setNan( &m_fX ); + ::rtl::math::setNan( &m_fY ); + ::rtl::math::setNan( &m_fZ ); + } + + double m_fX; + double m_fY; + double m_fZ; +}; + +void AreaChart::createShapes() +{ + if( m_aZSlots.begin() == m_aZSlots.end() ) //no series + return; + + if( m_nDimension == 2 && ( m_bArea || !m_bCategoryXAxis ) ) + lcl_reorderSeries( m_aZSlots ); + + DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"AreaChart is not proper initialized"); + if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is())) + return; + + //the text labels should be always on top of the other series shapes + //for area chart the error bars should be always on top of the other series shapes + + //therefore create an own group for the texts and the error bars to move them to front + //(because the text group is created after the series group the texts are displayed on top) + m_xSeriesTarget = createGroupShape( m_xLogicTarget,rtl::OUString() ); + if( m_bArea ) + m_xErrorBarTarget = createGroupShape( m_xLogicTarget,rtl::OUString() ); + else + m_xErrorBarTarget = m_xSeriesTarget; + m_xTextTarget = m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() ); + m_xRegressionCurveEquationTarget = m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() ); + + //--------------------------------------------- + //check necessary here that different Y axis can not be stacked in the same group? ... hm? + + //update/create information for current group + double fLogicZ = 0.5;//as defined + + sal_Int32 nStartIndex = 0; // inclusive ;..todo get somehow from x scale + sal_Int32 nEndIndex = VSeriesPlotter::getPointCount(); + if(nEndIndex<=0) + nEndIndex=1; + + //better performance for big data + std::map< VDataSeries*, FormerPoint > aSeriesFormerPointMap; + m_bPointsWereSkipped = false; + sal_Int32 nSkippedPoints = 0; + sal_Int32 nCreatedPoints = 0; + // + +//============================================================================= + //iterate through all x values per indices + for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ ) + { + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + + std::map< sal_Int32, double > aLogicYSumMap;//one for each different nAttachedAxisIndex + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + //iterate through all x slots in this category to get 100percent sum + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + VDataSeries* pSeries( *aSeriesIter ); + if(!pSeries) + continue; + + sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex(); + if( aLogicYSumMap.find(nAttachedAxisIndex)==aLogicYSumMap.end() ) + aLogicYSumMap[nAttachedAxisIndex]=0.0; + + PlottingPositionHelper* pPosHelper = &(this->getPlottingPositionHelper( nAttachedAxisIndex )); + if(!pPosHelper) + pPosHelper = m_pMainPosHelper; + PlotterBase::m_pPosHelper = pPosHelper; + + double fAdd = pSeries->getYValue( nIndex ); + if( !::rtl::math::isNan(fAdd) && !::rtl::math::isInf(fAdd) ) + aLogicYSumMap[nAttachedAxisIndex] += fabs( fAdd ); + } + } + } + +//============================================================================= + aZSlotIter = m_aZSlots.begin(); + for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + //for the area chart there should be at most one x slot (no side by side stacking available) + //attention different: xSlots are always interpreted as independent areas one behind the other: @todo this doesn't work why not??? + aXSlotIter = aZSlotIter->begin(); + for( sal_Int32 nX=0; aXSlotIter != aXSlotEnd; aXSlotIter++, nX++ ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + + std::map< sal_Int32, double > aLogicYForNextSeriesMap;//one for each different nAttachedAxisIndex + //============================================================================= + //iterate through all series + for( sal_Int32 nSeriesIndex = 0; aSeriesIter != aSeriesEnd; aSeriesIter++, nSeriesIndex++ ) + { + VDataSeries* pSeries( *aSeriesIter ); + if(!pSeries) + continue; + + /* #i70133# ignore points outside of series length in standard area + charts. Stacked area charts will use missing points as zeros. In + standard charts, pSeriesList contains only one series. */ + if( m_bArea && (pSeriesList->size() == 1) && (nIndex >= (*aSeriesIter)->getTotalPointCount()) ) + continue; + + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShapeFrontChild(*aSeriesIter, m_xSeriesTarget); + + sal_Int32 nAttachedAxisIndex = (*aSeriesIter)->getAttachedAxisIndex(); + PlottingPositionHelper* pPosHelper = &(this->getPlottingPositionHelper( nAttachedAxisIndex )); + if(!pPosHelper) + pPosHelper = m_pMainPosHelper; + PlotterBase::m_pPosHelper = pPosHelper; + + if(m_nDimension==3) + fLogicZ = nZ+0.5; + (*aSeriesIter)->m_fLogicZPos = fLogicZ; + + //collect data point information (logic coordinates, style ): + double fLogicX = (*aSeriesIter)->getXValue(nIndex); + double fLogicY = (*aSeriesIter)->getYValue(nIndex); + + if( m_bIsPolarCooSys && m_bArea && + ( ::rtl::math::isNan(fLogicY) || ::rtl::math::isInf(fLogicY) ) ) + { + if( (*aSeriesIter)->getMissingValueTreatment() == ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP ) + { + if( pSeriesList->size() == 1 || nSeriesIndex == 0 ) + { + fLogicY = pPosHelper->getLogicMinY(); + if( !pPosHelper->isMathematicalOrientationY() ) + fLogicY = pPosHelper->getLogicMaxY(); + } + else + fLogicY = 0.0; + } + } + + if( m_nDimension==3 && m_bArea && pSeriesList->size()!=1 ) + fLogicY = fabs( fLogicY ); + + if( pPosHelper->isPercentY() && !::rtl::math::approxEqual( aLogicYSumMap[nAttachedAxisIndex], 0.0 ) ) + { + fLogicY = fabs( fLogicY )/aLogicYSumMap[nAttachedAxisIndex]; + } + + if( ::rtl::math::isNan(fLogicX) || ::rtl::math::isInf(fLogicX) + || ::rtl::math::isNan(fLogicY) || ::rtl::math::isInf(fLogicY) + || ::rtl::math::isNan(fLogicZ) || ::rtl::math::isInf(fLogicZ) ) + { + if( (*aSeriesIter)->getMissingValueTreatment() == ::com::sun::star::chart::MissingValueTreatment::LEAVE_GAP ) + { + drawing::PolyPolygonShape3D& rPolygon = (*aSeriesIter)->m_aPolyPolygonShape3D; + sal_Int32& rIndex = (*aSeriesIter)->m_nPolygonIndex; + if( 0<= rIndex && rIndex < rPolygon.SequenceX.getLength() ) + { + if( rPolygon.SequenceX[ rIndex ].getLength() ) + rIndex++; //start a new polygon for the next point if the current poly is not empty + } + } + continue; + } + + if( aLogicYForNextSeriesMap.find(nAttachedAxisIndex) == aLogicYForNextSeriesMap.end() ) + aLogicYForNextSeriesMap[nAttachedAxisIndex] = 0.0; + + double fLogicValueForLabeDisplay = fLogicY; + + fLogicY += aLogicYForNextSeriesMap[nAttachedAxisIndex]; + aLogicYForNextSeriesMap[nAttachedAxisIndex] = fLogicY; + + bool bIsVisible = pPosHelper->isLogicVisible( fLogicX, fLogicY, fLogicZ ); + + //remind minimal and maximal x values for area 'grounding' points + //only for filled area + { + double& rfMinX = (*aSeriesIter)->m_fLogicMinX; + if(!nIndex||fLogicX<rfMinX) + rfMinX=fLogicX; + double& rfMaxX = (*aSeriesIter)->m_fLogicMaxX; + if(!nIndex||fLogicX>rfMaxX) + rfMaxX=fLogicX; + } + + drawing::Position3D aUnscaledLogicPosition( fLogicX, fLogicY, fLogicZ ); + drawing::Position3D aScaledLogicPosition(aUnscaledLogicPosition); + pPosHelper->doLogicScaling( aScaledLogicPosition ); + + //transformation 3) -> 4) + drawing::Position3D aScenePosition( pPosHelper->transformLogicToScene( fLogicX,fLogicY,fLogicZ, false ) ); + + //better performance for big data + FormerPoint aFormerPoint( aSeriesFormerPointMap[pSeries] ); + pPosHelper->setCoordinateSystemResolution( m_aCoordinateSystemResolution ); + if( !pSeries->isAttributedDataPoint(nIndex) + && + pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fY, aFormerPoint.m_fZ + , aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ ) ) + { + nSkippedPoints++; + m_bPointsWereSkipped = true; + continue; + } + aSeriesFormerPointMap[pSeries] = FormerPoint(aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ); + // + + //store point information for series polygon + //for area and/or line (symbols only do not need this) + if( isValidPosition(aScaledLogicPosition) ) + { + AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aScaledLogicPosition, (*aSeriesIter)->m_nPolygonIndex ); + + //prepare clipping for filled net charts + if( !bIsVisible && m_bIsPolarCooSys && m_bArea ) + { + drawing::Position3D aClippedPos(aScaledLogicPosition); + pPosHelper->clipScaledLogicValues( 0, &aClippedPos.PositionY, 0 ); + if( pPosHelper->isLogicVisible( aClippedPos.PositionX, aClippedPos.PositionY, aClippedPos.PositionZ ) ) + { + AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aClippedPos, (*aSeriesIter)->m_nPolygonIndex ); + AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aScaledLogicPosition, (*aSeriesIter)->m_nPolygonIndex ); + } + } + } + + //create a single datapoint if point is visible + //apply clipping: + if( !bIsVisible ) + continue; + + bool bCreateErrorBar = false; + { + uno::Reference< beans::XPropertySet > xErrorBarProp(pSeries->getYErrorBarProperties(nIndex)); + if( xErrorBarProp.is() ) + { + bool bShowPositive = false; + bool bShowNegative = false; + xErrorBarProp->getPropertyValue( C2U( "ShowPositiveError" )) >>= bShowPositive; + xErrorBarProp->getPropertyValue( C2U( "ShowNegativeError" )) >>= bShowNegative; + bCreateErrorBar = bShowPositive || bShowNegative; + } + } + + Symbol* pSymbolProperties = m_bSymbol ? (*aSeriesIter)->getSymbolProperties( nIndex ) : 0; + bool bCreateSymbol = pSymbolProperties && (pSymbolProperties->Style != SymbolStyle_NONE); + + if( !bCreateSymbol && !bCreateErrorBar && !pSeries->getDataPointLabelIfLabel(nIndex) ) + continue; + + //create a group shape for this point and add to the series shape: + rtl::OUString aPointCID = ObjectIdentifier::createPointCID( + (*aSeriesIter)->getPointCID_Stub(), nIndex ); + uno::Reference< drawing::XShapes > xPointGroupShape_Shapes( + createGroupShape(xSeriesGroupShape_Shapes,aPointCID) ); + uno::Reference<drawing::XShape> xPointGroupShape_Shape = + uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY ); + + { + nCreatedPoints++; + + //create data point + drawing::Direction3D aSymbolSize(0,0,0); + if( bCreateSymbol ) + { + if(m_nDimension!=3) + { + if( pSymbolProperties ) + { + if( pSymbolProperties->Style != SymbolStyle_NONE ) + { + aSymbolSize.DirectionX = pSymbolProperties->Size.Width; + aSymbolSize.DirectionY = pSymbolProperties->Size.Height; + } + + if( pSymbolProperties->Style == SymbolStyle_STANDARD ) + { + sal_Int32 nSymbol = pSymbolProperties->StandardSymbol; + m_pShapeFactory->createSymbol2D( xPointGroupShape_Shapes + , aScenePosition, aSymbolSize + , nSymbol + , pSymbolProperties->BorderColor + , pSymbolProperties->FillColor ); + } + else if( pSymbolProperties->Style == SymbolStyle_GRAPHIC ) + { + m_pShapeFactory->createGraphic2D( xPointGroupShape_Shapes + , aScenePosition , aSymbolSize + , pSymbolProperties->Graphic ); + } + //@todo other symbol styles + } + } + } + //create error bar + createErrorBar_Y( aUnscaledLogicPosition, **aSeriesIter, nIndex, m_xErrorBarTarget ); + + //create data point label + if( (**aSeriesIter).getDataPointLabelIfLabel(nIndex) ) + { + LabelAlignment eAlignment = LABEL_ALIGN_TOP; + drawing::Position3D aScenePosition3D( aScenePosition.PositionX + , aScenePosition.PositionY + , aScenePosition.PositionZ+this->getTransformedDepth() ); + + sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nIndex, m_xChartTypeModel, m_nDimension, pPosHelper->isSwapXAndY() ); + + switch(nLabelPlacement) + { + case ::com::sun::star::chart::DataLabelPlacement::TOP: + aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1); + eAlignment = LABEL_ALIGN_TOP; + break; + case ::com::sun::star::chart::DataLabelPlacement::BOTTOM: + aScenePosition3D.PositionY += (aSymbolSize.DirectionY/2+1); + eAlignment = LABEL_ALIGN_BOTTOM; + break; + case ::com::sun::star::chart::DataLabelPlacement::LEFT: + aScenePosition3D.PositionX -= (aSymbolSize.DirectionX/2+1); + eAlignment = LABEL_ALIGN_LEFT; + break; + case ::com::sun::star::chart::DataLabelPlacement::RIGHT: + aScenePosition3D.PositionX += (aSymbolSize.DirectionX/2+1); + eAlignment = LABEL_ALIGN_RIGHT; + break; + case ::com::sun::star::chart::DataLabelPlacement::CENTER: + eAlignment = LABEL_ALIGN_CENTER; + //todo implement this different for area charts + break; + default: + DBG_ERROR("this label alignment is not implemented yet"); + aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1); + eAlignment = LABEL_ALIGN_TOP; + break; + } + + awt::Point aScreenPosition2D;//get the screen position for the labels + sal_Int32 nOffset = 100; //todo maybe calculate this font height dependent + if( m_bIsPolarCooSys && nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::OUTSIDE ) + { + PolarPlottingPositionHelper* pPolarPosHelper = dynamic_cast<PolarPlottingPositionHelper*>(pPosHelper); + if( pPolarPosHelper ) + { + PolarLabelPositionHelper aPolarLabelPositionHelper(pPolarPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory); + aScreenPosition2D = awt::Point( aPolarLabelPositionHelper.getLabelScreenPositionAndAlignmentForLogicValues( + eAlignment, fLogicX, fLogicY, fLogicZ, nOffset )); + } + } + else + { + if(LABEL_ALIGN_CENTER==eAlignment || m_nDimension == 3 ) + nOffset = 0; + aScreenPosition2D = awt::Point( LabelPositionHelper(pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory) + .transformSceneToScreenPosition( aScenePosition3D ) ); + } + + this->createDataLabel( m_xTextTarget, **aSeriesIter, nIndex + , fLogicValueForLabeDisplay + , aLogicYSumMap[nAttachedAxisIndex], aScreenPosition2D, eAlignment, nOffset ); + } + } + + //remove PointGroupShape if empty + if(!xPointGroupShape_Shapes->getCount()) + xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape); + + }//next series in x slot (next y slot) + }//next x slot + }//next z slot + }//next category +//============================================================================= +//============================================================================= +//============================================================================= + + impl_createSeriesShapes(); + + /* @todo remove series shapes if empty + //remove and delete point-group-shape if empty + if(!xSeriesGroupShape_Shapes->getCount()) + { + (*aSeriesIter)->m_xShape.set(NULL); + m_xLogicTarget->remove(xSeriesGroupShape_Shape); + } + */ + + //remove and delete series-group-shape if empty + + //... todo + + OSL_TRACE( "\nPPPPPPPPP<<<<<<<<<<<< area chart :: createShapes():: skipped points: %d created points: %d", nSkippedPoints, nCreatedPoints ); +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/AreaChart.hxx b/chart2/source/view/charttypes/AreaChart.hxx new file mode 100644 index 000000000000..c3bd1ed3fbb4 --- /dev/null +++ b/chart2/source/view/charttypes/AreaChart.hxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_AREACHART_HXX +#define _CHART2_AREACHART_HXX + +#include "VSeriesPlotter.hxx" +#include <com/sun/star/chart2/CurveStyle.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. +class AreaPositionHelper; + +class AreaChart : public VSeriesPlotter +{ + //------------------------------------------------------------------------- + // public methods + //------------------------------------------------------------------------- +public: + AreaChart( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XChartType >& xChartTypeModel + , sal_Int32 nDimensionCount + , bool bCategoryXAxis, bool bNoArea=false + , PlottingPositionHelper* pPlottingPositionHelper=NULL //takes owner ship + , bool bConnectLastToFirstPoint=false + , bool bExpandIfValuesCloseToBorder=true + , sal_Int32 nKeepAspectRatio=-1 //0->no 1->yes other value->automatic + , const ::com::sun::star::drawing::Direction3D& rAspectRatio=::com::sun::star::drawing::Direction3D(1,1,1)//only taken into account if nKeepAspectRatio==1 + ); + virtual ~AreaChart(); + + //------------------------------------------------------------------------- + // chart2::XPlotter + //------------------------------------------------------------------------- + + virtual void SAL_CALL createShapes(); + /* + virtual ::rtl::OUString SAL_CALL getCoordinateSystemTypeID( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setScales( const ::com::sun::star::uno::Sequence< ::com::sun::star::chart2::ExplicitScaleData >& rScales ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setTransformation( const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToLogicTarget, const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToFinalPage ) throw (::com::sun::star::uno::RuntimeException); + */ + + virtual void addSeries( VDataSeries* pSeries, sal_Int32 zSlot = -1, sal_Int32 xSlot = -1,sal_Int32 ySlot = -1 ); + + //------------------- + virtual ::com::sun::star::drawing::Direction3D getPreferredDiagramAspectRatio() const; + virtual bool keepAspectRatio() const; + + //------------------------------------------------------------------------- + // MinimumAndMaximumSupplier + //------------------------------------------------------------------------- + virtual double getMinimumX(); + virtual double getMaximumX(); + virtual bool isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex ); + virtual bool isSeperateStackingForDifferentSigns( sal_Int32 nDimensionIndex ); + + //------------------------------------------------------------------------- + + virtual ::com::sun::star::chart2::LegendSymbolStyle getLegendSymbolStyle(); + virtual ::com::sun::star::uno::Any getExplicitSymbol( const VDataSeries& rSeries, sal_Int32 nPointIndex=-1/*-1 for series symbol*/ ); + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- +private: //methods + //no default constructor + AreaChart(); + + void impl_createSeriesShapes(); + bool impl_createArea( VDataSeries* pSeries + , ::com::sun::star::drawing::PolyPolygonShape3D* pSeriesPoly + , ::com::sun::star::drawing::PolyPolygonShape3D* pPreviousSeriesPoly + , PlottingPositionHelper* pPosHelper ); + bool impl_createLine( VDataSeries* pSeries + , ::com::sun::star::drawing::PolyPolygonShape3D* pSeriesPoly + , PlottingPositionHelper* pPosHelper ); + +private: //member + PlottingPositionHelper* m_pMainPosHelper; + + bool m_bArea;//false -> line or symbol only + bool m_bLine; + bool m_bSymbol; + bool m_bIsPolarCooSys;//used e.g. for net chart (the data labels need to be placed different) + bool m_bConnectLastToFirstPoint;//used e.g. for net chart + bool m_bExpandIfValuesCloseToBorder; // e.g. false for net charts + + sal_Int32 m_nKeepAspectRatio; //0->no 1->yes other value->automatic + ::com::sun::star::drawing::Direction3D m_aGivenAspectRatio; //only used if nKeepAspectRatio==1 + + //Properties for splines: + ::com::sun::star::chart2::CurveStyle m_eCurveStyle; + sal_Int32 m_nCurveResolution; + sal_Int32 m_nSplineOrder; + + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes > m_xSeriesTarget; + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes > m_xErrorBarTarget; + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes > m_xTextTarget; + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes > m_xRegressionCurveEquationTarget; +}; +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/BarChart.cxx b/chart2/source/view/charttypes/BarChart.cxx new file mode 100644 index 000000000000..cbcfd48f0f9d --- /dev/null +++ b/chart2/source/view/charttypes/BarChart.cxx @@ -0,0 +1,969 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "BarChart.hxx" +#include "ShapeFactory.hxx" +#include "CommonConverters.hxx" +#include "ObjectIdentifier.hxx" +#include "LabelPositionHelper.hxx" +#include "BarPositionHelper.hxx" +#include "macros.hxx" +#include "AxisIndexDefines.hxx" +#include "Clipping.hxx" + +#include <com/sun/star/chart/DataLabelPlacement.hpp> + +#include <com/sun/star/chart2/DataPointGeometry3D.hpp> +#include <tools/debug.hxx> +#include <rtl/math.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::rtl::math; +using namespace ::com::sun::star::chart2; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +BarChart::BarChart( const uno::Reference<XChartType>& xChartTypeModel + , sal_Int32 nDimensionCount ) + : VSeriesPlotter( xChartTypeModel, nDimensionCount ) + , m_pMainPosHelper( new BarPositionHelper() ) +{ + PlotterBase::m_pPosHelper = m_pMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper; + + try + { + if( m_xChartTypeModelProps.is() ) + { + m_xChartTypeModelProps->getPropertyValue( C2U( "OverlapSequence" ) ) >>= m_aOverlapSequence; + m_xChartTypeModelProps->getPropertyValue( C2U( "GapwidthSequence" ) ) >>= m_aGapwidthSequence; + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } +} + +BarChart::~BarChart() +{ + delete m_pMainPosHelper; +} + +//------------------------------------------------------------------------- + +PlottingPositionHelper& BarChart::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const +{ + PlottingPositionHelper& rPosHelper = VSeriesPlotter::getPlottingPositionHelper( nAxisIndex ); + + BarPositionHelper* pBarPosHelper = dynamic_cast<BarPositionHelper*>(&rPosHelper); + if( pBarPosHelper && nAxisIndex >= 0 ) + { + if( nAxisIndex < m_aOverlapSequence.getLength() ) + pBarPosHelper->setInnerDistance( -m_aOverlapSequence[nAxisIndex]/100.0 ); + if( nAxisIndex < m_aGapwidthSequence.getLength() ) + pBarPosHelper->setOuterDistance( m_aGapwidthSequence[nAxisIndex]/100.0 ); + } + + return rPosHelper; +} + +drawing::Direction3D BarChart::getPreferredDiagramAspectRatio() const +{ + drawing::Direction3D aRet(1.0,1.0,1.0); + if( m_nDimension == 3 ) + { + aRet = drawing::Direction3D(1.0,-1.0,1.0); + BarPositionHelper* pPosHelper = dynamic_cast<BarPositionHelper*>(&( this->getPlottingPositionHelper( MAIN_AXIS_INDEX) ) ); + drawing::Direction3D aScale( pPosHelper->getScaledLogicWidth() ); + if(aScale.DirectionX!=0.0) + { + double fXSlotCount = 1.0; + if(!m_aZSlots.empty()) + fXSlotCount = m_aZSlots.begin()->size(); + + aRet.DirectionZ = aScale.DirectionZ/(aScale.DirectionX + aScale.DirectionX*(fXSlotCount-1.0)*pPosHelper->getSlotWidth()); + } + else + return VSeriesPlotter::getPreferredDiagramAspectRatio(); + if(aRet.DirectionZ<0.05) + aRet.DirectionZ=0.05; + if(aRet.DirectionZ>10) + aRet.DirectionZ=10; + + if( m_pMainPosHelper && m_pMainPosHelper->isSwapXAndY() ) + { + double fTemp = aRet.DirectionX; + aRet.DirectionX = aRet.DirectionY; + aRet.DirectionY = fTemp; + } + } + else + aRet = drawing::Direction3D(-1,-1,-1); + return aRet; +} + +bool BarChart::keepAspectRatio() const +{ + if( m_nDimension == 3 ) + return true; + return true; +} + +//------------------------------------------------------------------------- +// MinimumAndMaximumSupplier +//------------------------------------------------------------------------- + +double BarChart::getMinimumX() +{ + if( m_bCategoryXAxis ) + return 0.5;//first category (index 0) matches with real number 1.0 + return VSeriesPlotter::getMinimumX(); +} +double BarChart::getMaximumX() +{ + if( m_bCategoryXAxis ) + { + //return category count + sal_Int32 nPointCount = getPointCount(); + return nPointCount+0.5;//first category (index 0) matches with real number 1.0 + } + return VSeriesPlotter::getMaximumX(); +} + +awt::Point BarChart::getLabelScreenPositionAndAlignment( + LabelAlignment& rAlignment, sal_Int32 nLabelPlacement + , double fScaledX, double fScaledLowerYValue, double fScaledUpperYValue, double fScaledZ + , double fScaledLowerBarDepth, double fScaledUpperBarDepth, double fBaseValue + , BarPositionHelper* pPosHelper + ) const +{ + double fX = fScaledX; + double fY = fScaledUpperYValue; + double fZ = fScaledZ; + bool bReverse = !pPosHelper->isMathematicalOrientationY(); + bool bNormalOutside = (!bReverse == !!(fBaseValue < fScaledUpperYValue)); + double fDepth = fScaledUpperBarDepth; + + switch(nLabelPlacement) + { + case ::com::sun::star::chart::DataLabelPlacement::TOP: + { + if( !pPosHelper->isSwapXAndY() ) + { + fY = bReverse ? fScaledLowerYValue : fScaledUpperYValue; + rAlignment = LABEL_ALIGN_TOP; + if(3==m_nDimension) + fDepth = bReverse ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth); + } + else + { + fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0; + rAlignment = LABEL_ALIGN_CENTER; + DBG_ERROR( "top label placement is not really supported by horizontal bar charts" ); + } + } + break; + case ::com::sun::star::chart::DataLabelPlacement::BOTTOM: + { + if(!pPosHelper->isSwapXAndY()) + { + fY = bReverse ? fScaledUpperYValue : fScaledLowerYValue; + rAlignment = LABEL_ALIGN_BOTTOM; + if(3==m_nDimension) + fDepth = bReverse ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth); + } + else + { + fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0; + rAlignment = LABEL_ALIGN_CENTER; + DBG_ERROR( "bottom label placement is not supported by horizontal bar charts" ); + } + } + break; + case ::com::sun::star::chart::DataLabelPlacement::LEFT: + { + if( pPosHelper->isSwapXAndY() ) + { + fY = bReverse ? fScaledUpperYValue : fScaledLowerYValue; + rAlignment = LABEL_ALIGN_LEFT; + if(3==m_nDimension) + fDepth = bReverse ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth); + } + else + { + fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0; + rAlignment = LABEL_ALIGN_CENTER; + DBG_ERROR( "left label placement is not supported by column charts" ); + } + } + break; + case ::com::sun::star::chart::DataLabelPlacement::RIGHT: + { + if( pPosHelper->isSwapXAndY() ) + { + fY = bReverse ? fScaledLowerYValue : fScaledUpperYValue; + rAlignment = LABEL_ALIGN_RIGHT; + if(3==m_nDimension) + fDepth = bReverse ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth); + } + else + { + fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0; + rAlignment = LABEL_ALIGN_CENTER; + DBG_ERROR( "right label placement is not supported by column charts" ); + } + } + break; + case ::com::sun::star::chart::DataLabelPlacement::OUTSIDE: + { + fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue; + if( pPosHelper->isSwapXAndY() ) + rAlignment = bNormalOutside ? LABEL_ALIGN_RIGHT : LABEL_ALIGN_LEFT; + else + rAlignment = bNormalOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM; + if(3==m_nDimension) + fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth); + } + break; + case ::com::sun::star::chart::DataLabelPlacement::INSIDE: + { + fY = (fBaseValue < fScaledUpperYValue) ? fScaledUpperYValue : fScaledLowerYValue; + if( pPosHelper->isSwapXAndY() ) + rAlignment = bNormalOutside ? LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; + else + rAlignment = bNormalOutside ? LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP; + if(3==m_nDimension) + fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledUpperBarDepth) : fabs(fScaledLowerBarDepth); + } + break; + case ::com::sun::star::chart::DataLabelPlacement::NEAR_ORIGIN: + { + fY = (fBaseValue < fScaledUpperYValue) ? fScaledLowerYValue : fScaledUpperYValue; + if( pPosHelper->isSwapXAndY() ) + rAlignment = bNormalOutside ? LABEL_ALIGN_RIGHT : LABEL_ALIGN_LEFT; + else + rAlignment = bNormalOutside ? LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM; + if(3==m_nDimension) + fDepth = (fBaseValue < fScaledUpperYValue) ? fabs(fScaledLowerBarDepth) : fabs(fScaledUpperBarDepth); + } + break; + case ::com::sun::star::chart::DataLabelPlacement::CENTER: + fY -= (fScaledUpperYValue-fScaledLowerYValue)/2.0; + rAlignment = LABEL_ALIGN_CENTER; + if(3==m_nDimension) + fDepth = fabs(fScaledUpperBarDepth-fScaledLowerBarDepth)/2.0; + break; + default: + DBG_ERROR("this label alignment is not implemented yet"); + + break; + } + if(3==m_nDimension) + fZ -= fDepth/2.0; + + drawing::Position3D aScenePosition3D( pPosHelper-> + transformScaledLogicToScene( fX, fY, fZ, true ) ); + return LabelPositionHelper(pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory) + .transformSceneToScreenPosition( aScenePosition3D ); +} + +uno::Reference< drawing::XShape > BarChart::createDataPoint3D_Bar( + const uno::Reference< drawing::XShapes >& xTarget + , const drawing::Position3D& rPosition, const drawing::Direction3D& rSize + , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree + , const uno::Reference< beans::XPropertySet >& xObjectProperties + , sal_Int32 nGeometry3D ) +{ + bool bRoundedEdges = true; + try + { + if( xObjectProperties.is() ) + { + sal_Int16 nPercentDiagonal = 0; + xObjectProperties->getPropertyValue( C2U( "PercentDiagonal" ) ) >>= nPercentDiagonal; + if( nPercentDiagonal < 5 ) + bRoundedEdges = false; + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + + uno::Reference< drawing::XShape > xShape(NULL); + switch( nGeometry3D ) + { + case DataPointGeometry3D::CYLINDER: + xShape = m_pShapeFactory->createCylinder( xTarget, rPosition, rSize, nRotateZAngleHundredthDegree ); + break; + case DataPointGeometry3D::CONE: + xShape = m_pShapeFactory->createCone( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree ); + break; + case DataPointGeometry3D::PYRAMID: + xShape = m_pShapeFactory->createPyramid( xTarget, rPosition, rSize, fTopHeight, nRotateZAngleHundredthDegree>0 + , xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + break; + case DataPointGeometry3D::CUBOID: + default: + xShape = m_pShapeFactory->createCube( xTarget, rPosition, rSize + , nRotateZAngleHundredthDegree, xObjectProperties + , PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), bRoundedEdges ); + return xShape; + } + if( nGeometry3D != DataPointGeometry3D::PYRAMID ) + this->setMappedProperties( xShape, xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + return xShape; +} + +namespace +{ +bool lcl_hasGeometry3DVariableWidth( sal_Int32 nGeometry3D ) +{ + bool bRet = false; + switch( nGeometry3D ) + { + case DataPointGeometry3D::PYRAMID: + case DataPointGeometry3D::CONE: + bRet = true; + break; + case DataPointGeometry3D::CUBOID: + case DataPointGeometry3D::CYLINDER: + default: + bRet = false; + break; + } + return bRet; +} +}// end anonymous namespace + +void BarChart::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot ) +{ + if( !pSeries ) + return; + if(m_nDimension==2) + { + //2ND_AXIS_IN_BARS put series on second scales to different z slot as temporary workaround + //this needs to be redesigned if 3d bars are also able to display secondary axes + + sal_Int32 nAxisIndex = pSeries->getAttachedAxisIndex(); + zSlot = nAxisIndex; + + if( !pSeries->getGroupBarsPerAxis() ) + zSlot = 0; + if(zSlot>=static_cast<sal_Int32>(m_aZSlots.size())) + m_aZSlots.resize(zSlot+1); + } + VSeriesPlotter::addSeries( pSeries, zSlot, xSlot, ySlot ); +} + +//better performance for big data +struct FormerBarPoint +{ + FormerBarPoint( double fX, double fUpperY, double fLowerY, double fZ ) + : m_fX(fX), m_fUpperY(fUpperY), m_fLowerY(fLowerY), m_fZ(fZ) + {} + FormerBarPoint() + { + ::rtl::math::setNan( &m_fX ); + ::rtl::math::setNan( &m_fUpperY ); + ::rtl::math::setNan( &m_fLowerY ); + ::rtl::math::setNan( &m_fZ ); + } + + double m_fX; + double m_fUpperY; + double m_fLowerY; + double m_fZ; +}; + +void BarChart::adaptOverlapAndGapwidthForGroupBarsPerAxis() +{ + //adapt m_aOverlapSequence and m_aGapwidthSequence for the groupBarsPerAxis feature + //thus the different series use the same settings + + VDataSeries* pFirstSeries = getFirstSeries(); + if(pFirstSeries && !pFirstSeries->getGroupBarsPerAxis() ) + { + sal_Int32 nAxisIndex = pFirstSeries->getAttachedAxisIndex(); + sal_Int32 nN = 0; + sal_Int32 nUseThisIndex = nAxisIndex; + if( nUseThisIndex < 0 || nUseThisIndex >= m_aOverlapSequence.getLength() ) + nUseThisIndex = 0; + for( nN = 0; nN < m_aOverlapSequence.getLength(); nN++ ) + { + if(nN!=nUseThisIndex) + m_aOverlapSequence[nN] = m_aOverlapSequence[nUseThisIndex]; + } + + nUseThisIndex = nAxisIndex; + if( nUseThisIndex < 0 || nUseThisIndex >= m_aGapwidthSequence.getLength() ) + nUseThisIndex = 0; + for( nN = 0; nN < m_aGapwidthSequence.getLength(); nN++ ) + { + if(nN!=nUseThisIndex) + m_aGapwidthSequence[nN] = m_aGapwidthSequence[nUseThisIndex]; + } + } +} + +void BarChart::createShapes() +{ + if( m_aZSlots.begin() == m_aZSlots.end() ) //no series + return; + + DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"BarChart is not proper initialized"); + if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is())) + return; + + //the text labels should be always on top of the other series shapes + //therefore create an own group for the texts to move them to front + //(because the text group is created after the series group the texts are displayed on top) + + //the regression curves should always be on top of the bars but beneath the text labels + //to achieve this the regression curve target is created after the series target and before the text target + + uno::Reference< drawing::XShapes > xSeriesTarget( + createGroupShape( m_xLogicTarget,rtl::OUString() )); + uno::Reference< drawing::XShapes > xRegressionCurveTarget( + createGroupShape( m_xLogicTarget,rtl::OUString() )); + uno::Reference< drawing::XShapes > xTextTarget( + m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() )); + + + //--------------------------------------------- + uno::Reference< drawing::XShapes > xRegressionCurveEquationTarget( + m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() )); + //check necessary here that different Y axis can not be stacked in the same group? ... hm? + + double fLogicZ = 0.0;//as defined + + bool bDrawConnectionLines = false; + bool bDrawConnectionLinesInited = false; + bool bOnlyConnectionLinesForThisPoint = false; + + adaptOverlapAndGapwidthForGroupBarsPerAxis(); + + //better performance for big data + std::map< VDataSeries*, FormerBarPoint > aSeriesFormerPointMap; + m_bPointsWereSkipped = false; + sal_Int32 nSkippedPoints = 0; + sal_Int32 nCreatedPoints = 0; + // + + //(@todo maybe different iteration for breaks in axis ?) + sal_Int32 nStartCategoryIndex = m_pMainPosHelper->getStartCategoryIndex(); // inclusive + sal_Int32 nEndCategoryIndex = m_pMainPosHelper->getEndCategoryIndex(); //inclusive +//============================================================================= + //iterate through all shown categories + for( sal_Int32 nCatIndex = nStartCategoryIndex; nCatIndex < nEndCategoryIndex; nCatIndex++ ) + { + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + + //sum up the values for all series in a complete z zlot per attached axis + ::std::map< sal_Int32, double > aLogicYSumMap; + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + for( aXSlotIter = aZSlotIter->begin(); aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + sal_Int32 nAttachedAxisIndex = aXSlotIter->getAttachedAxisIndexForFirstSeries(); + if( aLogicYSumMap.find(nAttachedAxisIndex)==aLogicYSumMap.end() ) + aLogicYSumMap[nAttachedAxisIndex]=0.0; + + double fMinimumY = 0.0, fMaximumY = 0.0; + aXSlotIter->calculateYMinAndMaxForCategory( nCatIndex + , isSeperateStackingForDifferentSigns( 1 ), fMinimumY, fMaximumY, nAttachedAxisIndex ); + + if( !::rtl::math::isNan( fMaximumY ) && fMaximumY > 0) + aLogicYSumMap[nAttachedAxisIndex] += fMaximumY; + if( !::rtl::math::isNan( fMinimumY ) && fMinimumY < 0) + aLogicYSumMap[nAttachedAxisIndex] += fabs(fMinimumY); + } + } + +//============================================================================= + aZSlotIter = m_aZSlots.begin(); + for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + +//============================================================================= + //iterate through all x slots in this category + double fSlotX=0; + for( aXSlotIter = aZSlotIter->begin(); aXSlotIter != aXSlotEnd; aXSlotIter++, fSlotX+=1.0 ) + { + sal_Int32 nAttachedAxisIndex = 0; + BarPositionHelper* pPosHelper = m_pMainPosHelper; + if( aXSlotIter != aXSlotEnd ) + { + nAttachedAxisIndex = aXSlotIter->getAttachedAxisIndexForFirstSeries(); + //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot + pPosHelper = dynamic_cast<BarPositionHelper*>(&( this->getPlottingPositionHelper( nAttachedAxisIndex ) ) ); + if(!pPosHelper) + pPosHelper = m_pMainPosHelper; + } + PlotterBase::m_pPosHelper = pPosHelper; + + //update/create information for current group + pPosHelper->updateSeriesCount( aZSlotIter->size() ); + double fLogicBaseWidth = pPosHelper->getSlotWidth(); + + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + + // get distance from base value to maximum and minimum + + double fMinimumY = 0.0, fMaximumY = 0.0; + aXSlotIter->calculateYMinAndMaxForCategory( nCatIndex + , isSeperateStackingForDifferentSigns( 1 ), fMinimumY, fMaximumY, nAttachedAxisIndex ); + + double fLogicPositiveYSum = 0.0; + if( !::rtl::math::isNan( fMaximumY ) ) + fLogicPositiveYSum = fMaximumY; + + double fLogicNegativeYSum = 0.0; + if( !::rtl::math::isNan( fMinimumY ) ) + fLogicNegativeYSum = fMinimumY; + + if( pPosHelper->isPercentY() ) + { + /* #i70395# fLogicPositiveYSum contains sum of all positive + values, if any, otherwise the highest negative value. + fLogicNegativeYSum contains sum of all negative values, + if any, otherwise the lowest positive value. + Afterwards, fLogicPositiveYSum will contain the maximum + (positive) value that is related to 100%. */ + + // do nothing if there are positive values only + if( fLogicNegativeYSum < 0.0 ) + { + // fLogicPositiveYSum<0 => negative values only, use absolute of negative sum + if( fLogicPositiveYSum < 0.0 ) + fLogicPositiveYSum = -fLogicNegativeYSum; + // otherwise there are positive and negative values, calculate total distance + else + fLogicPositiveYSum -= fLogicNegativeYSum; + } + fLogicNegativeYSum = 0.0; + } + + double fBaseValue = 0.0; + if( !pPosHelper->isPercentY() && pSeriesList->size()<=1 ) + fBaseValue = pPosHelper->getBaseValueY(); + double fPositiveLogicYForNextSeries = fBaseValue; + double fNegativeLogicYForNextSeries = fBaseValue; + + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + aSeriesIter = pSeriesList->begin(); + //============================================================================= + //iterate through all series in this x slot + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + VDataSeries* pSeries( *aSeriesIter ); + if(!pSeries) + continue; + + bOnlyConnectionLinesForThisPoint = false; + + if(nCatIndex==nStartCategoryIndex)//do not create a regression line for each point + createRegressionCurvesShapes( **aSeriesIter, xRegressionCurveTarget, xRegressionCurveEquationTarget, + m_pPosHelper->maySkipPointsInRegressionCalculation()); + + if( !bDrawConnectionLinesInited ) + { + bDrawConnectionLines = pSeries->getConnectBars(); + if( m_nDimension==3 ) + bDrawConnectionLines = false; + if( bDrawConnectionLines && pSeriesList->size()==1 ) + { + //detect wether we have a stacked chart or not: + StackingDirection eDirection = pSeries->getStackingDirection(); + if( eDirection != StackingDirection_Y_STACKING ) + bDrawConnectionLines = false; + } + bDrawConnectionLinesInited = true; + } + + //------------ + + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes( + getSeriesGroupShape(*aSeriesIter, xSeriesTarget) ); + + //collect data point information (logic coordinates, style ): + double fLogicX = pPosHelper->getSlotPos( (*aSeriesIter)->getXValue( nCatIndex ), fSlotX ); + double fLogicBarHeight = (*aSeriesIter)->getYValue( nCatIndex ); + if( ::rtl::math::isNan( fLogicBarHeight )) //no value at this category + continue; + + double fLogicValueForLabeDisplay = fLogicBarHeight; + fLogicBarHeight-=fBaseValue; + + if( pPosHelper->isPercentY() ) + { + if(fLogicPositiveYSum!=0.0) + fLogicBarHeight = fabs( fLogicBarHeight )/fLogicPositiveYSum; + else + fLogicBarHeight = 0.0; + } + + //sort negative and positive values, to display them on different sides of the x axis + bool bPositive = fLogicBarHeight >= 0.0; + double fLowerYValue = bPositive ? fPositiveLogicYForNextSeries : fNegativeLogicYForNextSeries; + double fUpperYValue = fLowerYValue+fLogicBarHeight; + if( bPositive ) + fPositiveLogicYForNextSeries += fLogicBarHeight; + else + fNegativeLogicYForNextSeries += fLogicBarHeight; + + if(m_nDimension==3) + fLogicZ = nZ; + + drawing::Position3D aUnscaledLogicPosition( fLogicX, fUpperYValue, fLogicZ ); + + //@todo ... start an iteration over the different breaks of the axis + //each subsystem may add an additional shape to form the whole point + //create a group shape for this point and add to the series shape: + // uno::Reference< drawing::XShapes > xPointGroupShape_Shapes( createGroupShape(xSeriesGroupShape_Shapes) ); + // uno::Reference<drawing::XShape> xPointGroupShape_Shape = + // uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY ); + //as long as we do not iterate we do not need to create an additional group for each point + uno::Reference< drawing::XShapes > xPointGroupShape_Shapes = xSeriesGroupShape_Shapes; + uno::Reference< beans::XPropertySet > xDataPointProperties( (*aSeriesIter)->getPropertiesOfPoint( nCatIndex ) ); + sal_Int32 nGeometry3D = DataPointGeometry3D::CUBOID; + if(m_nDimension==3) try + { + xDataPointProperties->getPropertyValue( C2U( "Geometry3D" )) >>= nGeometry3D; + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + + //@todo iterate through all subsystems to create partial points + { + //@todo select a suiteable PositionHelper for this subsystem + BarPositionHelper* pSubPosHelper = pPosHelper; + + double fUnclippedUpperYValue = fUpperYValue; + + //apply clipping to Y + if( !pPosHelper->clipYRange(fLowerYValue,fUpperYValue) ) + { + if( bDrawConnectionLines ) + bOnlyConnectionLinesForThisPoint = true; + else + continue; + } + //@todo clipping of X and Z is not fully integrated so far, as there is a need to create different objects + + //apply scaling to Y before calculating width (necessary to maintain gradient in clipped objects) + pSubPosHelper->doLogicScaling(NULL,&fLowerYValue,NULL); + pSubPosHelper->doLogicScaling(NULL,&fUpperYValue,NULL); + //scaling of X and Z is not provided as the created objects should be symmetric in that dimensions + + pSubPosHelper->doLogicScaling(NULL,&fUnclippedUpperYValue,NULL); + + //calculate resulting width + double fCompleteHeight = bPositive ? fLogicPositiveYSum : fLogicNegativeYSum; + if( pPosHelper->isPercentY() ) + fCompleteHeight = 1.0; + double fLogicBarWidth = fLogicBaseWidth; + double fTopHeight=approxSub(fCompleteHeight,fUpperYValue); + if(!bPositive) + fTopHeight=approxSub(fCompleteHeight,fLowerYValue); + double fLogicYStart = bPositive ? fLowerYValue : fUpperYValue; + double fMiddleHeight = fUpperYValue-fLowerYValue; + if(!bPositive) + fMiddleHeight*=-1.0; + double fLogicBarDepth = 0.5; + if(m_nDimension==3) + { + if( lcl_hasGeometry3DVariableWidth(nGeometry3D) && fCompleteHeight!=0.0 ) + { + double fHeight = fCompleteHeight-fLowerYValue; + if(!bPositive) + fHeight = fCompleteHeight-fUpperYValue; + fLogicBarWidth = fLogicBaseWidth*fHeight/(fCompleteHeight); + if(fLogicBarWidth<=0.0) + fLogicBarWidth=fLogicBaseWidth; + fLogicBarDepth = fLogicBarDepth*fHeight/(fCompleteHeight); + if(fLogicBarDepth<=0.0) + fLogicBarDepth*=-1.0; + } + } + + //better performance for big data + FormerBarPoint aFormerPoint( aSeriesFormerPointMap[pSeries] ); + pPosHelper->setCoordinateSystemResolution( m_aCoordinateSystemResolution ); + if( !pSeries->isAttributedDataPoint(nCatIndex) + && + pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fUpperY, aFormerPoint.m_fZ + , fLogicX, fUpperYValue, fLogicZ ) + && + pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fLowerY, aFormerPoint.m_fZ + , fLogicX, fLowerYValue, fLogicZ ) + ) + { + nSkippedPoints++; + m_bPointsWereSkipped = true; + continue; + } + aSeriesFormerPointMap[pSeries] = FormerBarPoint(fLogicX,fUpperYValue,fLowerYValue,fLogicZ); + // + + // + if( bDrawConnectionLines ) + { + //store point information for connection lines + + drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUnclippedUpperYValue,fLogicZ ); + drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUnclippedUpperYValue,fLogicZ ); + + if( isValidPosition(aLeftUpperPoint) ) + AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aLeftUpperPoint ); + if( isValidPosition(aRightUpperPoint) ) + AddPointToPoly( (*aSeriesIter)->m_aPolyPolygonShape3D, aRightUpperPoint ); + } + + if( bOnlyConnectionLinesForThisPoint ) + continue; + + //maybe additional possibility for performance improvement + //bool bCreateLineInsteadOfComplexGeometryDueToMissingSpace = false; + //pPosHelper->isSameForGivenResolution( fLogicX-fLogicBarWidth/2.0, fLowerYValue, fLogicZ + // , fLogicX+fLogicBarWidth/2.0, fLowerYValue, fLogicZ ); + + nCreatedPoints++; + //create partial point + if( !approxEqual(fLowerYValue,fUpperYValue) ) + { + uno::Reference< drawing::XShape > xShape; + if( m_nDimension==3 ) + { + drawing::Position3D aLogicBottom (fLogicX,fLogicYStart,fLogicZ); + drawing::Position3D aLogicLeftBottomFront (fLogicX+fLogicBarWidth/2.0,fLogicYStart,fLogicZ-fLogicBarDepth/2.0); + drawing::Position3D aLogicRightDeepTop (fLogicX-fLogicBarWidth/2.0,fLogicYStart+fMiddleHeight,fLogicZ+fLogicBarDepth/2.0); + drawing::Position3D aLogicTopTop (fLogicX,fLogicYStart+fMiddleHeight+fTopHeight,fLogicZ); + + uno::Reference< XTransformation > xTransformation = pSubPosHelper->getTransformationScaledLogicToScene(); + + //transformation 3) -> 4) + drawing::Position3D aTransformedBottom ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicBottom) ) ) ); + drawing::Position3D aTransformedLeftBottomFront ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicLeftBottomFront) ) ) ); + drawing::Position3D aTransformedRightDeepTop ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicRightDeepTop) ) ) ); + drawing::Position3D aTransformedTopTop ( SequenceToPosition3D( xTransformation->transform( Position3DToSequence(aLogicTopTop) ) ) ); + + drawing::Direction3D aSize = aTransformedRightDeepTop - aTransformedLeftBottomFront; + drawing::Direction3D aTopSize( aTransformedTopTop - aTransformedRightDeepTop ); + fTopHeight = aTopSize.DirectionY; + + sal_Int32 nRotateZAngleHundredthDegree = 0; + if( pPosHelper->isSwapXAndY() ) + { + fTopHeight = aTopSize.DirectionX; + nRotateZAngleHundredthDegree = 90*100; + aSize = drawing::Direction3D(aSize.DirectionY,aSize.DirectionX,aSize.DirectionZ); + } + + if( aSize.DirectionX < 0 ) + aSize.DirectionX *= -1.0; + if( aSize.DirectionZ < 0 ) + aSize.DirectionZ *= -1.0; + if( fTopHeight < 0 ) + fTopHeight *= -1.0; + + xShape = createDataPoint3D_Bar( + xPointGroupShape_Shapes, aTransformedBottom, aSize, fTopHeight, nRotateZAngleHundredthDegree + , xDataPointProperties, nGeometry3D ); + } + else //m_nDimension!=3 + { + drawing::PolyPolygonShape3D aPoly; + drawing::Position3D aLeftUpperPoint( fLogicX-fLogicBarWidth/2.0,fUpperYValue,fLogicZ ); + drawing::Position3D aRightUpperPoint( fLogicX+fLogicBarWidth/2.0,fUpperYValue,fLogicZ ); + + AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) ); + AddPointToPoly( aPoly, drawing::Position3D( fLogicX+fLogicBarWidth/2.0,fLowerYValue,fLogicZ) ); + AddPointToPoly( aPoly, aRightUpperPoint ); + AddPointToPoly( aPoly, aLeftUpperPoint ); + AddPointToPoly( aPoly, drawing::Position3D( fLogicX-fLogicBarWidth/2.0,fLowerYValue,fLogicZ) ); + pPosHelper->transformScaledLogicToScene( aPoly ); + xShape = m_pShapeFactory->createArea2D( xPointGroupShape_Shapes, aPoly ); + this->setMappedProperties( xShape, xDataPointProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + } + //set name/classified ObjectID (CID) + ShapeFactory::setShapeName(xShape + , ObjectIdentifier::createPointCID( + (*aSeriesIter)->getPointCID_Stub(),nCatIndex) ); + } + + //create error bar + createErrorBar_Y( aUnscaledLogicPosition, **aSeriesIter, nCatIndex, m_xLogicTarget ); + + //------------ + //create data point label + if( (**aSeriesIter).getDataPointLabelIfLabel(nCatIndex) ) + { + double fLogicSum = aLogicYSumMap[nAttachedAxisIndex]; + + LabelAlignment eAlignment(LABEL_ALIGN_CENTER); + sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nCatIndex, m_xChartTypeModel, m_nDimension, pPosHelper->isSwapXAndY() ); + + double fLowerBarDepth = fLogicBarDepth; + double fUpperBarDepth = fLogicBarDepth; + { + double fOuterBarDepth = fLogicBarDepth; + if( lcl_hasGeometry3DVariableWidth(nGeometry3D) && fCompleteHeight!=0.0 ) + { + fOuterBarDepth = fLogicBarDepth * (fTopHeight)/(fabs(fCompleteHeight)); + fLowerBarDepth = (fBaseValue < fUpperYValue) ? fabs(fLogicBarDepth) : fabs(fOuterBarDepth); + fUpperBarDepth = (fBaseValue < fUpperYValue) ? fabs(fOuterBarDepth) : fabs(fLogicBarDepth); + } + } + + awt::Point aScreenPosition2D( this->getLabelScreenPositionAndAlignment( + eAlignment, nLabelPlacement + , fLogicX, fLowerYValue, fUpperYValue, fLogicZ + , fLowerBarDepth, fUpperBarDepth, fBaseValue, pPosHelper )); + sal_Int32 nOffset = 0; + if(LABEL_ALIGN_CENTER!=eAlignment) + { + nOffset = 100;//add some spacing //@todo maybe get more intelligent values + if( m_nDimension == 3 ) + nOffset = 260; + } + this->createDataLabel( xTextTarget, **aSeriesIter, nCatIndex + , fLogicValueForLabeDisplay, fLogicSum, aScreenPosition2D, eAlignment, nOffset ); + } + + }//end iteration through partial points + + }//next series in x slot (next y slot) + }//next x slot + }//next z slot + }//next category +//============================================================================= +//============================================================================= +//============================================================================= + if( bDrawConnectionLines ) + { + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); +//============================================================================= + for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + BarPositionHelper* pPosHelper = m_pMainPosHelper; + if( aXSlotIter != aXSlotEnd ) + { + sal_Int32 nAttachedAxisIndex = aXSlotIter->getAttachedAxisIndexForFirstSeries(); + //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot + pPosHelper = dynamic_cast<BarPositionHelper*>(&( this->getPlottingPositionHelper( nAttachedAxisIndex ) ) ); + if(!pPosHelper) + pPosHelper = m_pMainPosHelper; + } + PlotterBase::m_pPosHelper = pPosHelper; + +//============================================================================= + //iterate through all x slots in this category + for( double fSlotX=0; aXSlotIter != aXSlotEnd; aXSlotIter++, fSlotX+=1.0 ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + aSeriesIter = pSeriesList->begin(); +//============================================================================= + //iterate through all series in this x slot + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + VDataSeries* pSeries( *aSeriesIter ); + if(!pSeries) + continue; + drawing::PolyPolygonShape3D* pSeriesPoly = &pSeries->m_aPolyPolygonShape3D; + if(!ShapeFactory::hasPolygonAnyLines(*pSeriesPoly)) + continue; + + drawing::PolyPolygonShape3D aPoly; + Clipping::clipPolygonAtRectangle( *pSeriesPoly, pPosHelper->getScaledLogicClipDoubleRect(), aPoly ); + + if(!ShapeFactory::hasPolygonAnyLines(aPoly)) + continue; + + //transformation 3) -> 4) + pPosHelper->transformScaledLogicToScene( aPoly ); + + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes( + getSeriesGroupShape(*aSeriesIter, xSeriesTarget) ); + uno::Reference< drawing::XShape > xShape( m_pShapeFactory->createLine2D( + xSeriesGroupShape_Shapes, PolyToPointSequence( aPoly ) ) ); + this->setMappedProperties( xShape, pSeries->getPropertiesOfSeries() + , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + } + } + } + } + + /* @todo remove series shapes if empty + //remove and delete point-group-shape if empty + if(!xSeriesGroupShape_Shapes->getCount()) + { + (*aSeriesIter)->m_xShape.set(NULL); + m_xLogicTarget->remove(xSeriesGroupShape_Shape); + } + */ + + //remove and delete series-group-shape if empty + + //... todo + + OSL_TRACE( "\nPPPPPPPPP<<<<<<<<<<<< bar chart :: createShapes():: skipped points: %d created points: %d", nSkippedPoints, nCreatedPoints ); +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/BarChart.hxx b/chart2/source/view/charttypes/BarChart.hxx new file mode 100644 index 000000000000..cfd3c0e618ce --- /dev/null +++ b/chart2/source/view/charttypes/BarChart.hxx @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_BARCHART_HXX +#define _CHART2_BARCHART_HXX + +#include "VSeriesPlotter.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. +class BarPositionHelper; + +class BarChart : public VSeriesPlotter +{ + //------------------------------------------------------------------------- + // public methods + //------------------------------------------------------------------------- +public: + BarChart( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XChartType >& xChartTypeModel + , sal_Int32 nDimensionCount ); + virtual ~BarChart(); + + //------------------------------------------------------------------------- + // chart2::XPlotter + //------------------------------------------------------------------------- + + virtual void SAL_CALL createShapes(); + /* + virtual ::rtl::OUString SAL_CALL getCoordinateSystemTypeID( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setScales( const ::com::sun::star::uno::Sequence< ::com::sun::star::chart2::ExplicitScaleData >& rScales ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setTransformation( const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToLogicTarget, const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToFinalPage ) throw (::com::sun::star::uno::RuntimeException); + */ + + virtual void addSeries( VDataSeries* pSeries, sal_Int32 zSlot = -1, sal_Int32 xSlot = -1,sal_Int32 ySlot = -1 ); + + //------------------- + virtual ::com::sun::star::drawing::Direction3D getPreferredDiagramAspectRatio() const; + virtual bool keepAspectRatio() const; + + //------------------------------------------------------------------------- + // MinimumAndMaximumSupplier + //------------------------------------------------------------------------- + virtual double getMinimumX(); + virtual double getMaximumX(); + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + +private: //methods + //no default constructor + BarChart(); + + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape > + createDataPoint3D_Bar( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes >& xTarget + , const ::com::sun::star::drawing::Position3D& rPosition + , const ::com::sun::star::drawing::Direction3D& rSize + , double fTopHeight, sal_Int32 nRotateZAngleHundredthDegree + , const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet >& xObjectProperties + , sal_Int32 nGeometry3D ); + + ::com::sun::star::awt::Point getLabelScreenPositionAndAlignment( + LabelAlignment& rAlignment, sal_Int32 nLabelPlacement + , double fScaledX, double fScaledLowerYValue, double fScaledUpperYValue, double fScaledZ + , double fScaledLowerBarDepth, double fScaledUpperBarDepth, double fBaseValue + , BarPositionHelper* pPosHelper ) const; + + virtual PlottingPositionHelper& getPlottingPositionHelper( sal_Int32 nAxisIndex ) const;//nAxisIndex indicates wether the position belongs to the main axis ( nAxisIndex==0 ) or secondary axis ( nAxisIndex==1 ) + + void adaptOverlapAndGapwidthForGroupBarsPerAxis(); + +private: //member + BarPositionHelper* m_pMainPosHelper; + ::com::sun::star::uno::Sequence< sal_Int32 > m_aOverlapSequence; + ::com::sun::star::uno::Sequence< sal_Int32 > m_aGapwidthSequence; +}; +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/BarPositionHelper.cxx b/chart2/source/view/charttypes/BarPositionHelper.cxx new file mode 100644 index 000000000000..271e0c5dd636 --- /dev/null +++ b/chart2/source/view/charttypes/BarPositionHelper.cxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "BarPositionHelper.hxx" +#include "Linear3DTransformation.hxx" +#include "ViewDefines.hxx" +#include "CommonConverters.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +BarPositionHelper::BarPositionHelper( bool /* bSwapXAndY */ ) + : CategoryPositionHelper( 1 ) +{ +} + +BarPositionHelper::BarPositionHelper( const BarPositionHelper& rSource ) + : CategoryPositionHelper( rSource ) + , PlottingPositionHelper( rSource ) +{ +} + +BarPositionHelper::~BarPositionHelper() +{ +} + +PlottingPositionHelper* BarPositionHelper::clone() const +{ + BarPositionHelper* pRet = new BarPositionHelper(*this); + return pRet; +} + +void BarPositionHelper::updateSeriesCount( double fSeriesCount ) +{ + m_fSeriesCount = fSeriesCount; +} + +uno::Reference< XTransformation > BarPositionHelper::getTransformationScaledLogicToScene() const +{ + //transformation from 2) to 4) //@todo 2) and 4) need a link to a document + + //we need to apply this transformation to each geometric object because of a bug/problem + //of the old drawing layer (the UNO_NAME_3D_EXTRUDE_DEPTH is an integer value instead of an double ) + + if( !m_xTransformationLogicToScene.is() ) + { + ::basegfx::B3DHomMatrix aMatrix; + + double MinX = getLogicMinX(); + double MinY = getLogicMinY(); + double MinZ = getLogicMinZ(); + double MaxX = getLogicMaxX(); + double MaxY = getLogicMaxY(); + double MaxZ = getLogicMaxZ(); + + AxisOrientation nXAxisOrientation = m_aScales[0].Orientation; + AxisOrientation nYAxisOrientation = m_aScales[1].Orientation; + AxisOrientation nZAxisOrientation = m_aScales[2].Orientation; + + //apply scaling + //scaling of x axis is refused/ignored + doLogicScaling( NULL, &MinY, &MinZ ); + doLogicScaling( NULL, &MaxY, &MaxZ); + + if(m_bSwapXAndY) + { + std::swap(MinX,MinY); + std::swap(MaxX,MaxY); + std::swap(nXAxisOrientation,nYAxisOrientation); + } + + if( AxisOrientation_MATHEMATICAL==nXAxisOrientation ) + aMatrix.translate(-MinX,0.0,0.0); + else + aMatrix.translate(-MaxX,0.0,0.0); + if( AxisOrientation_MATHEMATICAL==nYAxisOrientation ) + aMatrix.translate(0.0,-MinY,0.0); + else + aMatrix.translate(0.0,-MaxY,0.0); + if( AxisOrientation_MATHEMATICAL==nZAxisOrientation ) + aMatrix.translate(0.0,0.0,-MaxZ);//z direction in draw is reverse mathematical direction + else + aMatrix.translate(0.0,0.0,-MinZ); + + double fWidthX = MaxX - MinX; + double fWidthY = MaxY - MinY; + double fWidthZ = MaxZ - MinZ; + + double fScaleDirectionX = AxisOrientation_MATHEMATICAL==nXAxisOrientation ? 1.0 : -1.0; + double fScaleDirectionY = AxisOrientation_MATHEMATICAL==nYAxisOrientation ? 1.0 : -1.0; + double fScaleDirectionZ = AxisOrientation_MATHEMATICAL==nZAxisOrientation ? -1.0 : 1.0; + + aMatrix.scale(fScaleDirectionX*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthX + , fScaleDirectionY*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthY + , fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ); + + //if(nDim==2) + aMatrix = m_aMatrixScreenToScene*aMatrix; + + m_xTransformationLogicToScene = new Linear3DTransformation(B3DHomMatrixToHomogenMatrix( aMatrix ),m_bSwapXAndY); + } + return m_xTransformationLogicToScene; +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/BarPositionHelper.hxx b/chart2/source/view/charttypes/BarPositionHelper.hxx new file mode 100644 index 000000000000..293a277cd332 --- /dev/null +++ b/chart2/source/view/charttypes/BarPositionHelper.hxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_BARPOSITIONHELPER_HXX +#define _CHART2_BARPOSITIONHELPER_HXX + +#include "PlottingPositionHelper.hxx" +#include "CategoryPositionHelper.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class BarPositionHelper : public CategoryPositionHelper, public PlottingPositionHelper +{ +public: + BarPositionHelper( bool bSwapXAndY=true ); + BarPositionHelper( const BarPositionHelper& rSource ); + virtual ~BarPositionHelper(); + + virtual PlottingPositionHelper* clone() const; + + virtual ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation > + getTransformationScaledLogicToScene() const; + + void updateSeriesCount( double fSeriesCount ); /*only enter the size of x stacked series*/ + + sal_Int32 getStartCategoryIndex() const { + //first category (index 0) matches with real number 1.0 + sal_Int32 nStart = static_cast<sal_Int32>(getLogicMinX() - 0.5); + if( nStart < 0 ) + nStart = 0; + return nStart; + } + sal_Int32 getEndCategoryIndex() const { + //first category (index 0) matches with real number 1.0 + sal_Int32 nEnd = static_cast<sal_Int32>(getLogicMaxX() - 0.5); + if( nEnd < 0 ) + nEnd = 0; + return nEnd; + } +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/BubbleChart.cxx b/chart2/source/view/charttypes/BubbleChart.cxx new file mode 100644 index 000000000000..27b952938b82 --- /dev/null +++ b/chart2/source/view/charttypes/BubbleChart.cxx @@ -0,0 +1,420 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "BubbleChart.hxx" +#include "PlottingPositionHelper.hxx" +#include "ShapeFactory.hxx" +#include "CommonConverters.hxx" +#include "macros.hxx" +#include "ViewDefines.hxx" +#include "ObjectIdentifier.hxx" +#include "Splines.hxx" +#include "LabelPositionHelper.hxx" +#include "Clipping.hxx" +#include "Stripe.hxx" + +#include <com/sun/star/chart2/Symbol.hpp> +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <tools/debug.hxx> +#include <editeng/unoprnms.hxx> +#include <rtl/math.hxx> +#include <com/sun/star/drawing/DoubleSequence.hpp> +#include <com/sun/star/drawing/NormalsKind.hpp> +#include <com/sun/star/lang/XServiceName.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::rtl::math; +using namespace ::com::sun::star::chart2; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +BubbleChart::BubbleChart( const uno::Reference<XChartType>& xChartTypeModel + , sal_Int32 nDimensionCount ) + : VSeriesPlotter( xChartTypeModel, nDimensionCount, false ) + , m_bShowNegativeValues(false) + , m_bBubbleSizeAsArea(true) + , m_fBubbleSizeScaling(1.0) + , m_fMaxLogicBubbleSize( 0.0 ) + , m_fBubbleSizeFactorToScreen( 1.0 ) +{ + if( !m_pMainPosHelper ) + m_pMainPosHelper = new PlottingPositionHelper(); + PlotterBase::m_pPosHelper = m_pMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper; +} + +BubbleChart::~BubbleChart() +{ + delete m_pMainPosHelper; +} + +void BubbleChart::calculateMaximumLogicBubbleSize() +{ + double fMaxSize = 0.0; + + sal_Int32 nStartIndex = 0; + sal_Int32 nEndIndex = VSeriesPlotter::getPointCount(); + for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ ) + { + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + VDataSeries* pSeries( *aSeriesIter ); + if(!pSeries) + continue; + + double fSize = pSeries->getBubble_Size( nIndex ); + if( m_bShowNegativeValues ) + fSize = fabs(fSize); + if( fSize > fMaxSize ) + fMaxSize = fSize; + } + } + } + } + + m_fMaxLogicBubbleSize = fMaxSize; +} + +void BubbleChart::calculateBubbleSizeScalingFactor() +{ + double fLogicZ=0.5; + drawing::Position3D aSceneMinPos( m_pMainPosHelper->transformLogicToScene( m_pMainPosHelper->getLogicMinX(),m_pMainPosHelper->getLogicMinY(),fLogicZ, false ) ); + drawing::Position3D aSceneMaxPos( m_pMainPosHelper->transformLogicToScene( m_pMainPosHelper->getLogicMaxX(),m_pMainPosHelper->getLogicMaxY(),fLogicZ, false ) ); + + awt::Point aScreenMinPos( LabelPositionHelper(m_pMainPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory).transformSceneToScreenPosition( aSceneMinPos ) ); + awt::Point aScreenMaxPos( LabelPositionHelper(m_pMainPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory).transformSceneToScreenPosition( aSceneMaxPos ) ); + + sal_Int32 nWidth = abs( aScreenMaxPos.X - aScreenMinPos.X ); + sal_Int32 nHeight = abs( aScreenMaxPos.Y - aScreenMinPos.Y ); + + sal_Int32 nMinExtend = std::min( nWidth, nHeight ); + m_fBubbleSizeFactorToScreen = nMinExtend * 0.25;//max bubble size is 25 percent of diagram size +} + +drawing::Direction3D BubbleChart::transformToScreenBubbleSize( double fLogicSize ) +{ + drawing::Direction3D aRet(0,0,0); + + if( ::rtl::math::isNan(fLogicSize) || ::rtl::math::isInf(fLogicSize) ) + return aRet; + + if( m_bShowNegativeValues ) + fLogicSize = fabs(fLogicSize); + + double fMaxSize = m_fMaxLogicBubbleSize; + + double fMaxRadius = fMaxSize; + double fRaduis = fLogicSize; + if( m_bBubbleSizeAsArea ) + { + fMaxRadius = sqrt( fMaxSize / F_PI ); + fRaduis = sqrt( fLogicSize / F_PI ); + } + + aRet.DirectionX = m_fBubbleSizeScaling * m_fBubbleSizeFactorToScreen * fRaduis / fMaxRadius; + aRet.DirectionY = aRet.DirectionX; + + return aRet; +} + +bool BubbleChart::isExpandIfValuesCloseToBorder( sal_Int32 /*nDimensionIndex*/ ) +{ + return true; +} + +bool BubbleChart::isSeperateStackingForDifferentSigns( sal_Int32 /*nDimensionIndex*/ ) +{ + return false; +} + +//----------------------------------------------------------------- + +LegendSymbolStyle BubbleChart::getLegendSymbolStyle() +{ + return chart2::LegendSymbolStyle_CIRCLE; +} + +drawing::Direction3D BubbleChart::getPreferredDiagramAspectRatio() const +{ + return drawing::Direction3D(-1,-1,-1); +} + +void BubbleChart::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot ) +{ + VSeriesPlotter::addSeries( pSeries, zSlot, xSlot, ySlot ); +} + +//better performance for big data +struct FormerPoint +{ + FormerPoint( double fX, double fY, double fZ ) + : m_fX(fX), m_fY(fY), m_fZ(fZ) + {} + FormerPoint() + { + ::rtl::math::setNan( &m_fX ); + ::rtl::math::setNan( &m_fY ); + ::rtl::math::setNan( &m_fZ ); + } + + double m_fX; + double m_fY; + double m_fZ; +}; + +void BubbleChart::createShapes() +{ + if( m_aZSlots.begin() == m_aZSlots.end() ) //no series + return; + + DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"BubbleChart is not proper initialized"); + if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is())) + return; + + //therefore create an own group for the texts and the error bars to move them to front + //(because the text group is created after the series group the texts are displayed on top) + uno::Reference< drawing::XShapes > xSeriesTarget( + createGroupShape( m_xLogicTarget,rtl::OUString() )); + uno::Reference< drawing::XShapes > xTextTarget( + m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() )); + + //update/create information for current group + double fLogicZ = 0.5;//as defined + + sal_Int32 nStartIndex = 0; // inclusive ;..todo get somehow from x scale + sal_Int32 nEndIndex = VSeriesPlotter::getPointCount(); + if(nEndIndex<=0) + nEndIndex=1; + + //better performance for big data + std::map< VDataSeries*, FormerPoint > aSeriesFormerPointMap; + m_bPointsWereSkipped = false; + sal_Int32 nSkippedPoints = 0; + sal_Int32 nCreatedPoints = 0; + // + + calculateMaximumLogicBubbleSize(); + calculateBubbleSizeScalingFactor(); + if( m_fMaxLogicBubbleSize <= 0 || m_fBubbleSizeFactorToScreen <= 0 ) + return; + +//============================================================================= + //iterate through all x values per indices + for( sal_Int32 nIndex = nStartIndex; nIndex < nEndIndex; nIndex++ ) + { + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + + aZSlotIter = m_aZSlots.begin(); + for( sal_Int32 nZ=1; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + aXSlotIter = aZSlotIter->begin(); + for( sal_Int32 nX=0; aXSlotIter != aXSlotEnd; aXSlotIter++, nX++ ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + + //============================================================================= + //iterate through all series + for( sal_Int32 nSeriesIndex = 0; aSeriesIter != aSeriesEnd; aSeriesIter++, nSeriesIndex++ ) + { + VDataSeries* pSeries( *aSeriesIter ); + if(!pSeries) + continue; + + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShape(*aSeriesIter, xSeriesTarget); + + sal_Int32 nAttachedAxisIndex = pSeries->getAttachedAxisIndex(); + PlottingPositionHelper* pPosHelper = &(this->getPlottingPositionHelper( nAttachedAxisIndex )); + if(!pPosHelper) + pPosHelper = m_pMainPosHelper; + PlotterBase::m_pPosHelper = pPosHelper; + + if(m_nDimension==3) + fLogicZ = nZ+0.5; + + //collect data point information (logic coordinates, style ): + double fLogicX = pSeries->getXValue(nIndex); + double fLogicY = pSeries->getYValue(nIndex); + double fBubbleSize = pSeries->getBubble_Size( nIndex ); + + if( !m_bShowNegativeValues && fBubbleSize<0.0 ) + continue; + + if( ::rtl::math::approxEqual( fBubbleSize, 0.0 ) || ::rtl::math::isNan(fBubbleSize) ) + continue; + + if( ::rtl::math::isNan(fLogicX) || ::rtl::math::isInf(fLogicX) + || ::rtl::math::isNan(fLogicY) || ::rtl::math::isInf(fLogicY) ) + continue; + + bool bIsVisible = pPosHelper->isLogicVisible( fLogicX, fLogicY, fLogicZ ); + + drawing::Position3D aUnscaledLogicPosition( fLogicX, fLogicY, fLogicZ ); + drawing::Position3D aScaledLogicPosition(aUnscaledLogicPosition); + pPosHelper->doLogicScaling( aScaledLogicPosition ); + + //transformation 3) -> 4) + drawing::Position3D aScenePosition( pPosHelper->transformLogicToScene( fLogicX,fLogicY,fLogicZ, false ) ); + + //better performance for big data + FormerPoint aFormerPoint( aSeriesFormerPointMap[pSeries] ); + pPosHelper->setCoordinateSystemResolution( m_aCoordinateSystemResolution ); + if( !pSeries->isAttributedDataPoint(nIndex) + && + pPosHelper->isSameForGivenResolution( aFormerPoint.m_fX, aFormerPoint.m_fY, aFormerPoint.m_fZ + , aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ ) ) + { + nSkippedPoints++; + m_bPointsWereSkipped = true; + continue; + } + aSeriesFormerPointMap[pSeries] = FormerPoint(aScaledLogicPosition.PositionX, aScaledLogicPosition.PositionY, aScaledLogicPosition.PositionZ); + + //create a single datapoint if point is visible + if( !bIsVisible ) + continue; + + //create a group shape for this point and add to the series shape: + rtl::OUString aPointCID = ObjectIdentifier::createPointCID( + pSeries->getPointCID_Stub(), nIndex ); + uno::Reference< drawing::XShapes > xPointGroupShape_Shapes( + createGroupShape(xSeriesGroupShape_Shapes,aPointCID) ); + uno::Reference<drawing::XShape> xPointGroupShape_Shape = + uno::Reference<drawing::XShape>( xPointGroupShape_Shapes, uno::UNO_QUERY ); + + { + nCreatedPoints++; + + //create data point + drawing::Direction3D aSymbolSize = transformToScreenBubbleSize( fBubbleSize ); + if(m_nDimension!=3) + { + uno::Reference<drawing::XShape> xShape; + xShape = m_pShapeFactory->createCircle2D( xPointGroupShape_Shapes + , aScenePosition, aSymbolSize ); + + this->setMappedProperties( xShape + , pSeries->getPropertiesOfPoint( nIndex ) + , PropertyMapper::getPropertyNameMapForFilledSeriesProperties() ); + + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + } + + //create data point label + if( (**aSeriesIter).getDataPointLabelIfLabel(nIndex) ) + { + LabelAlignment eAlignment = LABEL_ALIGN_TOP; + drawing::Position3D aScenePosition3D( aScenePosition.PositionX + , aScenePosition.PositionY + , aScenePosition.PositionZ+this->getTransformedDepth() ); + + sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nIndex, m_xChartTypeModel, m_nDimension, pPosHelper->isSwapXAndY() ); + + switch(nLabelPlacement) + { + case ::com::sun::star::chart::DataLabelPlacement::TOP: + aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1); + eAlignment = LABEL_ALIGN_TOP; + break; + case ::com::sun::star::chart::DataLabelPlacement::BOTTOM: + aScenePosition3D.PositionY += (aSymbolSize.DirectionY/2+1); + eAlignment = LABEL_ALIGN_BOTTOM; + break; + case ::com::sun::star::chart::DataLabelPlacement::LEFT: + aScenePosition3D.PositionX -= (aSymbolSize.DirectionX/2+1); + eAlignment = LABEL_ALIGN_LEFT; + break; + case ::com::sun::star::chart::DataLabelPlacement::RIGHT: + aScenePosition3D.PositionX += (aSymbolSize.DirectionX/2+1); + eAlignment = LABEL_ALIGN_RIGHT; + break; + case ::com::sun::star::chart::DataLabelPlacement::CENTER: + eAlignment = LABEL_ALIGN_CENTER; + break; + default: + DBG_ERROR("this label alignment is not implemented yet"); + aScenePosition3D.PositionY -= (aSymbolSize.DirectionY/2+1); + eAlignment = LABEL_ALIGN_TOP; + break; + } + + + awt::Point aScreenPosition2D( LabelPositionHelper(pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory) + .transformSceneToScreenPosition( aScenePosition3D ) ); + sal_Int32 nOffset = 0; + if(LABEL_ALIGN_CENTER!=eAlignment) + nOffset = 100;//add some spacing //@todo maybe get more intelligent values + this->createDataLabel( xTextTarget, **aSeriesIter, nIndex + , fBubbleSize, fBubbleSize, aScreenPosition2D, eAlignment, nOffset ); + } + } + + //remove PointGroupShape if empty + if(!xPointGroupShape_Shapes->getCount()) + xSeriesGroupShape_Shapes->remove(xPointGroupShape_Shape); + + }//next series in x slot (next y slot) + }//next x slot + }//next z slot + }//next category +//============================================================================= +//============================================================================= +//============================================================================= + OSL_TRACE( "\nPPPPPPPPP<<<<<<<<<<<< area chart :: createShapes():: skipped points: %d created points: %d", nSkippedPoints, nCreatedPoints ); +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/BubbleChart.hxx b/chart2/source/view/charttypes/BubbleChart.hxx new file mode 100644 index 000000000000..2216d6499f7a --- /dev/null +++ b/chart2/source/view/charttypes/BubbleChart.hxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_BUBBLECHART_HXX +#define _CHART2_BUBBLECHART_HXX + +#include "VSeriesPlotter.hxx" +#include <com/sun/star/drawing/Direction3D.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. + +class BubbleChart : public VSeriesPlotter +{ + //------------------------------------------------------------------------- + // public methods + //------------------------------------------------------------------------- +public: + BubbleChart( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XChartType >& xChartTypeModel + , sal_Int32 nDimensionCount ); + virtual ~BubbleChart(); + + //------------------------------------------------------------------------- + // chart2::XPlotter + //------------------------------------------------------------------------- + + virtual void SAL_CALL createShapes(); + + virtual void addSeries( VDataSeries* pSeries, sal_Int32 zSlot = -1, sal_Int32 xSlot = -1,sal_Int32 ySlot = -1 ); + + //------------------- + virtual ::com::sun::star::drawing::Direction3D getPreferredDiagramAspectRatio() const; + + //------------------------------------------------------------------------- + // MinimumAndMaximumSupplier + //------------------------------------------------------------------------- + virtual bool isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex ); + virtual bool isSeperateStackingForDifferentSigns( sal_Int32 nDimensionIndex ); + + //------------------------------------------------------------------------- + + virtual ::com::sun::star::chart2::LegendSymbolStyle getLegendSymbolStyle(); + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- +private: //methods + //no default constructor + BubbleChart(); + + void calculateMaximumLogicBubbleSize(); + void calculateBubbleSizeScalingFactor(); + + com::sun::star::drawing::Direction3D transformToScreenBubbleSize( double fLogicSize ); + +private: //member + + bool m_bShowNegativeValues;//input parameter + bool m_bBubbleSizeAsArea;//input parameter + double m_fBubbleSizeScaling;//input parameter + + double m_fMaxLogicBubbleSize;//calculated values + double m_fBubbleSizeFactorToScreen;//calculated values +}; +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/CandleStickChart.cxx b/chart2/source/view/charttypes/CandleStickChart.cxx new file mode 100644 index 000000000000..971545c98415 --- /dev/null +++ b/chart2/source/view/charttypes/CandleStickChart.cxx @@ -0,0 +1,372 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "CandleStickChart.hxx" +#include "ShapeFactory.hxx" +#include "CommonConverters.hxx" +#include "ObjectIdentifier.hxx" +#include "LabelPositionHelper.hxx" +#include "BarPositionHelper.hxx" +#include "macros.hxx" +#include "VLegendSymbolFactory.hxx" +#include "FormattedStringHelper.hxx" +#include "DataSeriesHelper.hxx" +#include <tools/debug.hxx> +#include <rtl/math.hxx> +#include <editeng/unoprnms.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::rtl::math; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; +using ::rtl::OUString; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +CandleStickChart::CandleStickChart( const uno::Reference<XChartType>& xChartTypeModel + , sal_Int32 nDimensionCount ) + : VSeriesPlotter( xChartTypeModel, nDimensionCount ) + , m_pMainPosHelper( new BarPositionHelper() ) +{ + PlotterBase::m_pPosHelper = m_pMainPosHelper; + VSeriesPlotter::m_pMainPosHelper = m_pMainPosHelper; +} + +CandleStickChart::~CandleStickChart() +{ + delete m_pMainPosHelper; +} + +//------------------------------------------------------------------------- +// MinimumAndMaximumSupplier +//------------------------------------------------------------------------- + +double CandleStickChart::getMinimumX() +{ + if( m_bCategoryXAxis ) + return 0.5;//first category (index 0) matches with real number 1.0 + return VSeriesPlotter::getMinimumX(); +} +double CandleStickChart::getMaximumX() +{ + if( m_bCategoryXAxis ) + { + //return category count + sal_Int32 nPointCount = getPointCount(); + return nPointCount+0.5;//first category (index 0) matches with real number 1.0 + } + return VSeriesPlotter::getMaximumX(); +} +bool CandleStickChart::isSeperateStackingForDifferentSigns( sal_Int32 /* nDimensionIndex */ ) +{ + return false; +} + +//----------------------------------------------------------------- +//----------------------------------------------------------------- +//----------------------------------------------------------------- + +LegendSymbolStyle CandleStickChart::getLegendSymbolStyle() +{ + return chart2::LegendSymbolStyle_VERTICAL_LINE; +} + +drawing::Direction3D CandleStickChart::getPreferredDiagramAspectRatio() const +{ + return drawing::Direction3D(-1,-1,-1); +} + +void CandleStickChart::addSeries( VDataSeries* pSeries, sal_Int32 /* zSlot */, sal_Int32 xSlot, sal_Int32 ySlot ) +{ + //ignore y stacking for candle stick chart + VSeriesPlotter::addSeries( pSeries, 0, xSlot, ySlot ); +} + +void CandleStickChart::createShapes() +{ + if( m_aZSlots.begin() == m_aZSlots.end() ) //no series + return; + + if( m_nDimension!=2 ) + return; + + DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"CandleStickChart is not proper initialized"); + if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is())) + return; + + //the text labels should be always on top of the other series shapes + //therefore create an own group for the texts to move them to front + //(because the text group is created after the series group the texts are displayed on top) + + uno::Reference< drawing::XShapes > xSeriesTarget( + createGroupShape( m_xLogicTarget,rtl::OUString() )); + uno::Reference< drawing::XShapes > xLossTarget( + createGroupShape( m_xLogicTarget, ObjectIdentifier::createClassifiedIdentifier( + OBJECTTYPE_DATA_STOCK_LOSS, rtl::OUString() ))); + uno::Reference< drawing::XShapes > xGainTarget( + createGroupShape( m_xLogicTarget, ObjectIdentifier::createClassifiedIdentifier( + OBJECTTYPE_DATA_STOCK_GAIN, rtl::OUString() ))); + uno::Reference< drawing::XShapes > xTextTarget( + m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() )); + + //--------------------------------------------- + //check necessary here that different Y axis can not be stacked in the same group? ... hm? + + bool bJapaneseStyle=true;//@todo is this the correct default? + bool bShowFirst = true;//is only important if bJapaneseStyle == false + tNameSequence aWhiteBox_Names, aBlackBox_Names; + tAnySequence aWhiteBox_Values, aBlackBox_Values; + try + { + if( m_xChartTypeModelProps.is() ) + { + m_xChartTypeModelProps->getPropertyValue( C2U( "ShowFirst" ) ) >>= bShowFirst; + + uno::Reference< beans::XPropertySet > xWhiteDayProps(0); + uno::Reference< beans::XPropertySet > xBlackDayProps(0); + m_xChartTypeModelProps->getPropertyValue( C2U( "Japanese" ) ) >>= bJapaneseStyle; + m_xChartTypeModelProps->getPropertyValue( C2U( "WhiteDay" ) ) >>= xWhiteDayProps; + m_xChartTypeModelProps->getPropertyValue( C2U( "BlackDay" ) ) >>= xBlackDayProps; + + tPropertyNameValueMap aWhiteBox_Map; + PropertyMapper::getValueMap( aWhiteBox_Map, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xWhiteDayProps ); + PropertyMapper::getMultiPropertyListsFromValueMap( aWhiteBox_Names, aWhiteBox_Values, aWhiteBox_Map ); + + tPropertyNameValueMap aBlackBox_Map; + PropertyMapper::getValueMap( aBlackBox_Map, PropertyMapper::getPropertyNameMapForFillAndLineProperties(), xBlackDayProps ); + PropertyMapper::getMultiPropertyListsFromValueMap( aBlackBox_Names, aBlackBox_Values, aBlackBox_Map ); + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + + //(@todo maybe different iteration for breaks in axis ?) + sal_Int32 nStartCategoryIndex = m_pMainPosHelper->getStartCategoryIndex(); // inclusive + sal_Int32 nEndCategoryIndex = m_pMainPosHelper->getEndCategoryIndex(); //inclusive +//============================================================================= + //iterate through all shown categories + for( sal_Int32 nIndex = nStartCategoryIndex; nIndex < nEndCategoryIndex; nIndex++ ) + { + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); +//============================================================================= + for( sal_Int32 nZ=0; aZSlotIter != aZSlotEnd; aZSlotIter++, nZ++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + sal_Int32 nAttachedAxisIndex = 0; + BarPositionHelper* pPosHelper = m_pMainPosHelper; + if( aXSlotIter != aXSlotEnd ) + { + nAttachedAxisIndex = aXSlotIter->getAttachedAxisIndexForFirstSeries(); + //2ND_AXIS_IN_BARS so far one can assume to have the same plotter for each z slot + pPosHelper = dynamic_cast<BarPositionHelper*>(&( this->getPlottingPositionHelper( nAttachedAxisIndex ) ) ); + if(!pPosHelper) + pPosHelper = m_pMainPosHelper; + } + PlotterBase::m_pPosHelper = pPosHelper; + + //update/create information for current group + pPosHelper->updateSeriesCount( aZSlotIter->size() ); +//============================================================================= + //iterate through all x slots in this category + for( double fSlotX=0; aXSlotIter != aXSlotEnd; aXSlotIter++, fSlotX+=1.0 ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + aSeriesIter = pSeriesList->begin(); + //============================================================================= + //iterate through all series in this x slot + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + //collect data point information (logic coordinates, style ): + double fLogicX = pPosHelper->getSlotPos( (*aSeriesIter)->getXValue( nIndex ), fSlotX ); + double fY_First = (*aSeriesIter)->getY_First( nIndex ); + double fY_Last = (*aSeriesIter)->getY_Last( nIndex ); + double fY_Min = (*aSeriesIter)->getY_Min( nIndex ); + double fY_Max = (*aSeriesIter)->getY_Max( nIndex ); + + bool bBlack=false; + if(fY_Last<=fY_First) + { + std::swap(fY_First,fY_Last); + bBlack=true; + } + if(fY_Max<fY_Min) + std::swap(fY_Min,fY_Max); + //transformation 3) -> 4) + double fHalfWidth = pPosHelper->getSlotWidth()/2.0; + drawing::Position3D aPosLeftFirst( pPosHelper->transformLogicToScene( fLogicX-fHalfWidth, fY_First ,0 ,true ) ); + drawing::Position3D aPosRightLast( pPosHelper->transformLogicToScene( fLogicX+fHalfWidth, fY_Last ,0 ,true ) ); + drawing::Position3D aPosMiddleFirst( pPosHelper->transformLogicToScene( fLogicX, fY_First ,0 ,true ) ); + drawing::Position3D aPosMiddleLast( pPosHelper->transformLogicToScene( fLogicX, fY_Last ,0 ,true ) ); + drawing::Position3D aPosMiddleMinimum( pPosHelper->transformLogicToScene( fLogicX, fY_Min ,0 ,true ) ); + drawing::Position3D aPosMiddleMaximum( pPosHelper->transformLogicToScene( fLogicX, fY_Max ,0 ,true ) ); + + uno::Reference< drawing::XShapes > xLossGainTarget( xGainTarget ); + if(bBlack) + xLossGainTarget = xLossTarget; + + uno::Reference< beans::XPropertySet > xPointProp( (*aSeriesIter)->getPropertiesOfPoint( nIndex )); + uno::Reference< drawing::XShapes > xPointGroupShape_Shapes(0); + { + rtl::OUString aPointCID = ObjectIdentifier::createPointCID( (*aSeriesIter)->getPointCID_Stub(), nIndex ); + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes( getSeriesGroupShape(*aSeriesIter, xSeriesTarget) ); + xPointGroupShape_Shapes = createGroupShape(xSeriesGroupShape_Shapes,aPointCID); + } + + //create min-max line + if( isValidPosition(aPosMiddleMinimum) && isValidPosition(aPosMiddleMaximum) ) + { + uno::Reference< drawing::XShape > xShape( m_xShapeFactory->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.drawing.PolyLineShape" ) ) ), uno::UNO_QUERY ); + xPointGroupShape_Shapes->add(xShape); + uno::Reference< beans::XPropertySet > xProp( xShape, uno::UNO_QUERY ); + if(xProp.is()) + { + drawing::PolyPolygonShape3D aPoly; + sal_Int32 nLineIndex =0; + AddPointToPoly( aPoly, aPosMiddleMinimum, nLineIndex); + AddPointToPoly( aPoly, aPosMiddleMaximum, nLineIndex); + xProp->setPropertyValue( C2U( UNO_NAME_POLYPOLYGON ), uno::makeAny( PolyToPointSequence(aPoly) ) ); + } + this->setMappedProperties( xShape, xPointProp, PropertyMapper::getPropertyNameMapForLineSeriesProperties() ); + } + + //create first-last shape + if(bJapaneseStyle && isValidPosition(aPosLeftFirst) && isValidPosition(aPosRightLast) ) + { + uno::Reference< drawing::XShape > xShape( m_xShapeFactory->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.drawing.RectangleShape" ) ) ), uno::UNO_QUERY ); + xLossGainTarget->add(xShape); + + xShape->setPosition( Position3DToAWTPoint( aPosLeftFirst ) ); + drawing::Direction3D aDiff = aPosRightLast-aPosLeftFirst; + awt::Size aAWTSize( Direction3DToAWTSize( aDiff )); + // workaround for bug in drawing: if height is 0 the box gets infinitely large + if( aAWTSize.Height == 0 ) + aAWTSize.Height = 1; + xShape->setSize( aAWTSize ); + + uno::Reference< beans::XPropertySet > xProp( xShape, uno::UNO_QUERY ); + if(xProp.is()) + { + if(bBlack) + PropertyMapper::setMultiProperties( aBlackBox_Names, aBlackBox_Values, xProp ); + else + PropertyMapper::setMultiProperties( aWhiteBox_Names, aWhiteBox_Values, xProp ); + } + } + else + { + drawing::PolyPolygonShape3D aPoly; + + sal_Int32 nLineIndex = 0; + if( bShowFirst && pPosHelper->isLogicVisible( fLogicX, fY_First ,0 ) + && isValidPosition(aPosLeftFirst) && isValidPosition(aPosMiddleFirst) ) + { + AddPointToPoly( aPoly, aPosLeftFirst, nLineIndex ); + AddPointToPoly( aPoly, aPosMiddleFirst, nLineIndex++ ); + } + if( pPosHelper->isLogicVisible( fLogicX, fY_Last ,0 ) + && isValidPosition(aPosMiddleLast) && isValidPosition(aPosRightLast) ) + { + AddPointToPoly( aPoly, aPosMiddleLast, nLineIndex ); + AddPointToPoly( aPoly, aPosRightLast, nLineIndex ); + } + + if( aPoly.SequenceX.getLength() ) + { + uno::Reference< drawing::XShape > xShape( m_xShapeFactory->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( + "com.sun.star.drawing.PolyLineShape" ) ) ), uno::UNO_QUERY ); + xPointGroupShape_Shapes->add(xShape); + uno::Reference< beans::XPropertySet > xProp( xShape, uno::UNO_QUERY ); + if(xProp.is()) + { + xProp->setPropertyValue( C2U( UNO_NAME_POLYPOLYGON ), uno::makeAny( PolyToPointSequence(aPoly) ) ); + this->setMappedProperties( xShape, xPointProp, PropertyMapper::getPropertyNameMapForLineSeriesProperties() ); + } + } + } + + //create data point label + if( (**aSeriesIter).getDataPointLabelIfLabel(nIndex) ) + { + if(isValidPosition(aPosMiddleFirst)) + this->createDataLabel( xTextTarget, **aSeriesIter, nIndex + , fY_First, 1.0, Position3DToAWTPoint(aPosMiddleFirst), LABEL_ALIGN_LEFT_BOTTOM ); + if(isValidPosition(aPosMiddleLast)) + this->createDataLabel( xTextTarget, **aSeriesIter, nIndex + , fY_Last, 1.0, Position3DToAWTPoint(aPosMiddleLast), LABEL_ALIGN_RIGHT_TOP ); + if(isValidPosition(aPosMiddleMinimum)) + this->createDataLabel( xTextTarget, **aSeriesIter, nIndex + , fY_Min, 1.0, Position3DToAWTPoint(aPosMiddleMinimum), LABEL_ALIGN_BOTTOM ); + if(isValidPosition(aPosMiddleMaximum)) + this->createDataLabel( xTextTarget, **aSeriesIter, nIndex + , fY_Max, 1.0, Position3DToAWTPoint(aPosMiddleMaximum), LABEL_ALIGN_TOP ); + } + }//next series in x slot (next y slot) + }//next x slot + }//next z slot + }//next category +//============================================================================= +//============================================================================= +//============================================================================= + /* @todo remove series shapes if empty + //remove and delete point-group-shape if empty + if(!xSeriesGroupShape_Shapes->getCount()) + { + (*aSeriesIter)->m_xShape.set(NULL); + m_xLogicTarget->remove(xSeriesGroupShape_Shape); + } + */ + + //remove and delete series-group-shape if empty + + //... todo +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/CandleStickChart.hxx b/chart2/source/view/charttypes/CandleStickChart.hxx new file mode 100644 index 000000000000..cd33e54af5e9 --- /dev/null +++ b/chart2/source/view/charttypes/CandleStickChart.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_CANDLESTICKCHART_HXX +#define _CHART2_CANDLESTICKCHART_HXX + +#include "VSeriesPlotter.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. +class BarPositionHelper; + +class CandleStickChart : public VSeriesPlotter +{ + //------------------------------------------------------------------------- + // public methods + //------------------------------------------------------------------------- +public: + CandleStickChart( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XChartType >& xChartTypeModel + , sal_Int32 nDimensionCount ); + virtual ~CandleStickChart(); + + //------------------------------------------------------------------------- + // chart2::XPlotter + //------------------------------------------------------------------------- + + virtual void SAL_CALL createShapes(); + /* + virtual ::rtl::OUString SAL_CALL getCoordinateSystemTypeID( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setScales( const ::com::sun::star::uno::Sequence< ::com::sun::star::chart2::ExplicitScaleData >& rScales ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setTransformation( const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToLogicTarget, const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToFinalPage ) throw (::com::sun::star::uno::RuntimeException); + */ + virtual void addSeries( VDataSeries* pSeries, sal_Int32 zSlot = -1, sal_Int32 xSlot = -1,sal_Int32 ySlot = -1 ); + + virtual ::com::sun::star::drawing::Direction3D getPreferredDiagramAspectRatio() const; + + //------------------------------------------------------------------------- + // MinimumAndMaximumSupplier + //------------------------------------------------------------------------- + virtual double getMinimumX(); + virtual double getMaximumX(); + virtual bool isSeperateStackingForDifferentSigns( sal_Int32 nDimensionIndex ); + + //------------------------------------------------------------------------- + + virtual ::com::sun::star::chart2::LegendSymbolStyle getLegendSymbolStyle(); + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + +private: //methods + //no default constructor + CandleStickChart(); + +private: //member + BarPositionHelper* m_pMainPosHelper; +}; +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/CategoryPositionHelper.cxx b/chart2/source/view/charttypes/CategoryPositionHelper.cxx new file mode 100644 index 000000000000..657004706e81 --- /dev/null +++ b/chart2/source/view/charttypes/CategoryPositionHelper.cxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "CategoryPositionHelper.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. +//using namespace ::com::sun::star; +//using namespace ::com::sun::star::chart2; + +CategoryPositionHelper::CategoryPositionHelper( double fSeriesCount, double fCategoryWidth ) + : m_fSeriesCount(fSeriesCount) + , m_fCategoryWidth(fCategoryWidth) + , m_fInnerDistance(0.0) + , m_fOuterDistance(1.0) +{ +} + +CategoryPositionHelper::CategoryPositionHelper( const CategoryPositionHelper& rSource ) + : m_fSeriesCount( rSource.m_fSeriesCount ) + , m_fCategoryWidth( rSource.m_fCategoryWidth ) + , m_fInnerDistance( rSource.m_fInnerDistance ) + , m_fOuterDistance( rSource.m_fOuterDistance ) +{ +} + +CategoryPositionHelper::~CategoryPositionHelper() +{ +} + +double CategoryPositionHelper::getSlotWidth() const +{ + double fWidth = m_fCategoryWidth / + ( m_fSeriesCount + + m_fOuterDistance + + m_fInnerDistance*( m_fSeriesCount - 1.0) ); + return fWidth; +} + +double CategoryPositionHelper::getSlotPos( double fCategoryX, double fSeriesNumber ) const +{ + //the returned position is in the middle of the rect + //fSeriesNumber 0...n-1 + double fPos = fCategoryX - (m_fCategoryWidth/2.0) + + (m_fOuterDistance/2.0 + fSeriesNumber*(1.0+m_fInnerDistance)) * getSlotWidth() + + getSlotWidth()/2.0; + + return fPos; +} + +void CategoryPositionHelper::setInnerDistance( double fInnerDistance ) +{ + if( fInnerDistance < -1.0 ) + fInnerDistance = -1.0; + if( fInnerDistance > 1.0 ) + fInnerDistance = 1.0; + m_fInnerDistance = fInnerDistance; +} + +void CategoryPositionHelper::setOuterDistance( double fOuterDistance ) +{ + if( fOuterDistance < 0.0 ) + fOuterDistance = 0.0; + if( fOuterDistance > 6.0 ) + fOuterDistance = 6.0; + m_fOuterDistance = fOuterDistance; +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/CategoryPositionHelper.hxx b/chart2/source/view/charttypes/CategoryPositionHelper.hxx new file mode 100644 index 000000000000..c48fbf98b150 --- /dev/null +++ b/chart2/source/view/charttypes/CategoryPositionHelper.hxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_CATEGORYPOSITIONHELPER_HXX +#define _CHART2_CATEGORYPOSITIONHELPER_HXX + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class CategoryPositionHelper +{ +public: + CategoryPositionHelper( double fSeriesCount, double CategoryWidth = 1.0); + CategoryPositionHelper( const CategoryPositionHelper& rSource ); + virtual ~CategoryPositionHelper(); + + double getSlotWidth() const; + double getSlotPos( double fCategoryX, double fSeriesNumber ) const; + + //Distance between two neighboring bars in same category, seen relative to width of the bar + void setInnerDistance( double fInnerDistance ); + + //Distance between two neighboring bars in different category, seen relative to width of the bar: + void setOuterDistance( double fOuterDistance ); + +protected: + double m_fSeriesCount; + double m_fCategoryWidth; + //Distance between two neighboring bars in same category, seen relative to width of the bar: + double m_fInnerDistance; //[-1,1] m_fInnerDistance=1 --> distance == width; m_fInnerDistance=-1-->all rects are painted on the same position + //Distance between two neighboring bars in different category, seen relative to width of the bar: + double m_fOuterDistance; //>=0 m_fOuterDistance=1 --> distance == width +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/PieChart.cxx b/chart2/source/view/charttypes/PieChart.cxx new file mode 100644 index 000000000000..edb9e08b8018 --- /dev/null +++ b/chart2/source/view/charttypes/PieChart.cxx @@ -0,0 +1,883 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "PieChart.hxx" +#include "PlottingPositionHelper.hxx" +#include "ShapeFactory.hxx" +#include "PolarLabelPositionHelper.hxx" +#include "macros.hxx" +#include "CommonConverters.hxx" +#include "ViewDefines.hxx" +#include "ObjectIdentifier.hxx" + +#include <com/sun/star/chart/DataLabelPlacement.hpp> +#include <com/sun/star/chart2/XColorScheme.hpp> + +#include <com/sun/star/container/XChild.hpp> + +#include <tools/debug.hxx> +#include <rtl/math.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +class PiePositionHelper : public PolarPlottingPositionHelper +{ +public: + PiePositionHelper( NormalAxis eNormalAxis, double fAngleDegreeOffset ); + virtual ~PiePositionHelper(); + + bool getInnerAndOuterRadius( double fCategoryX, double& fLogicInnerRadius, double& fLogicOuterRadius, bool bUseRings, double fMaxOffset ) const; + +public: + //Distance between different category rings, seen relative to width of a ring: + double m_fRingDistance; //>=0 m_fRingDistance=1 --> distance == width +}; + +PiePositionHelper::PiePositionHelper( NormalAxis eNormalAxis, double fAngleDegreeOffset ) + : PolarPlottingPositionHelper(eNormalAxis) + , m_fRingDistance(0.0) +{ + m_fRadiusOffset = 0.0; + m_fAngleDegreeOffset = fAngleDegreeOffset; +} + +PiePositionHelper::~PiePositionHelper() +{ +} + +bool PiePositionHelper::getInnerAndOuterRadius( double fCategoryX + , double& fLogicInnerRadius, double& fLogicOuterRadius + , bool bUseRings, double fMaxOffset ) const +{ + if( !bUseRings ) + fCategoryX = 1.0; + + bool bIsVisible = true; + double fLogicInner = fCategoryX -0.5+m_fRingDistance/2.0; + double fLogicOuter = fCategoryX +0.5-m_fRingDistance/2.0; + + if( !isMathematicalOrientationRadius() ) + { + //in this case the given getMaximumX() was not corrcect instead the minimum should have been smaller by fMaxOffset + //but during getMaximumX and getMimumX we do not know the axis orientation + fLogicInner += fMaxOffset; + fLogicOuter += fMaxOffset; + } + + if( fLogicInner >= getLogicMaxX() ) + return false; + if( fLogicOuter <= getLogicMinX() ) + return false; + + if( fLogicInner < getLogicMinX() ) + fLogicInner = getLogicMinX(); + if( fLogicOuter > getLogicMaxX() ) + fLogicOuter = getLogicMaxX(); + + fLogicInnerRadius = fLogicInner; + fLogicOuterRadius = fLogicOuter; + if( !isMathematicalOrientationRadius() ) + std::swap(fLogicInnerRadius,fLogicOuterRadius); + return bIsVisible; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +PieChart::PieChart( const uno::Reference<XChartType>& xChartTypeModel + , sal_Int32 nDimensionCount + , bool bExcludingPositioning ) + : VSeriesPlotter( xChartTypeModel, nDimensionCount ) + , m_pPosHelper( new PiePositionHelper( NormalAxis_Z, (m_nDimension==3)?0.0:90.0 ) ) + , m_bUseRings(false) + , m_bSizeExcludesLabelsAndExplodedSegments(bExcludingPositioning) +{ + ::rtl::math::setNan(&m_fMaxOffset); + + PlotterBase::m_pPosHelper = m_pPosHelper; + VSeriesPlotter::m_pMainPosHelper = m_pPosHelper; + m_pPosHelper->m_fRadiusOffset = 0.0; + m_pPosHelper->m_fRingDistance = 0.0; + + uno::Reference< beans::XPropertySet > xChartTypeProps( xChartTypeModel, uno::UNO_QUERY ); + if( xChartTypeProps.is() ) try + { + xChartTypeProps->getPropertyValue( C2U( "UseRings" )) >>= m_bUseRings; + if( m_bUseRings ) + { + m_pPosHelper->m_fRadiusOffset = 1.0; + if( nDimensionCount==3 ) + m_pPosHelper->m_fRingDistance = 0.1; + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } +} + +PieChart::~PieChart() +{ + delete m_pPosHelper; +} + +//----------------------------------------------------------------- + +void SAL_CALL PieChart::setScales( const uno::Sequence< ExplicitScaleData >& rScales + , sal_Bool /* bSwapXAndYAxis */ ) + throw (uno::RuntimeException) +{ + DBG_ASSERT(m_nDimension<=rScales.getLength(),"Dimension of Plotter does not fit two dimension of given scale sequence"); + m_pPosHelper->setScales( rScales, true ); +} + +//----------------------------------------------------------------- + +drawing::Direction3D PieChart::getPreferredDiagramAspectRatio() const +{ + if( m_nDimension == 3 ) + return drawing::Direction3D(1,1,0.25); + return drawing::Direction3D(1,1,1); +} + +bool PieChart::keepAspectRatio() const +{ + if( m_nDimension == 3 ) + return false; + return true; +} + +bool PieChart::shouldSnapRectToUsedArea() +{ + return true; +} + +uno::Reference< drawing::XShape > PieChart::createDataPoint( + const uno::Reference< drawing::XShapes >& xTarget + , const uno::Reference< beans::XPropertySet >& xObjectProperties + , double fUnitCircleStartAngleDegree, double fUnitCircleWidthAngleDegree + , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius + , double fLogicZ, double fDepth, double fExplodePercentage + , tPropertyNameValueMap* pOverwritePropertiesMap ) +{ + //--------------------------- + //transform position: + drawing::Direction3D aOffset; + if( !::rtl::math::approxEqual( fExplodePercentage, 0.0 ) ) + { + double fAngle = fUnitCircleStartAngleDegree + fUnitCircleWidthAngleDegree/2.0; + double fRadius = (fUnitCircleOuterRadius-fUnitCircleInnerRadius)*fExplodePercentage; + drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene( 0, 0, fLogicZ ); + drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, fRadius, fLogicZ ); + aOffset = aNewOrigin - aOrigin; + } + + //--------------------------- + //create point + uno::Reference< drawing::XShape > xShape(0); + if(m_nDimension==3) + { + xShape = m_pShapeFactory->createPieSegment( xTarget + , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree + , fUnitCircleInnerRadius, fUnitCircleOuterRadius + , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() ) + , fDepth ); + } + else + { + xShape = m_pShapeFactory->createPieSegment2D( xTarget + , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree + , fUnitCircleInnerRadius, fUnitCircleOuterRadius + , aOffset, B3DHomMatrixToHomogenMatrix( m_pPosHelper->getUnitCartesianToScene() ) ); + } + this->setMappedProperties( xShape, xObjectProperties, PropertyMapper::getPropertyNameMapForFilledSeriesProperties(), pOverwritePropertiesMap ); + return xShape; +} + +void PieChart::addSeries( VDataSeries* pSeries, sal_Int32 /* zSlot */, sal_Int32 /* xSlot */, sal_Int32 /* ySlot */ ) +{ + VSeriesPlotter::addSeries( pSeries, 0, -1, 0 ); +} + +double PieChart::getMinimumX() +{ + return 0.5; +} +double PieChart::getMaxOffset() +{ + if (!::rtl::math::isNan(m_fMaxOffset)) + // Value already cached. Use it. + return m_fMaxOffset; + + m_fMaxOffset = 0.0; + if( m_aZSlots.size()<=0 ) + return m_fMaxOffset; + if( m_aZSlots[0].size()<=0 ) + return m_fMaxOffset; + + const ::std::vector< VDataSeries* >& rSeriesList( m_aZSlots[0][0].m_aSeriesVector ); + if( rSeriesList.size()<=0 ) + return m_fMaxOffset; + + VDataSeries* pSeries = rSeriesList[0]; + uno::Reference< beans::XPropertySet > xSeriesProp( pSeries->getPropertiesOfSeries() ); + if( !xSeriesProp.is() ) + return m_fMaxOffset; + + double fExplodePercentage=0.0; + xSeriesProp->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage; + if(fExplodePercentage>m_fMaxOffset) + m_fMaxOffset=fExplodePercentage; + + if(!m_bSizeExcludesLabelsAndExplodedSegments) + { + uno::Sequence< sal_Int32 > aAttributedDataPointIndexList; + if( xSeriesProp->getPropertyValue( C2U( "AttributedDataPoints" ) ) >>= aAttributedDataPointIndexList ) + { + for(sal_Int32 nN=aAttributedDataPointIndexList.getLength();nN--;) + { + uno::Reference< beans::XPropertySet > xPointProp( pSeries->getPropertiesOfPoint(aAttributedDataPointIndexList[nN]) ); + if(xPointProp.is()) + { + fExplodePercentage=0.0; + xPointProp->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage; + if(fExplodePercentage>m_fMaxOffset) + m_fMaxOffset=fExplodePercentage; + } + } + } + } + return m_fMaxOffset; +} +double PieChart::getMaximumX() +{ + double fMaxOffset = getMaxOffset(); + if( m_aZSlots.size()>0 && m_bUseRings) + return m_aZSlots[0].size()+0.5+fMaxOffset; + return 1.5+fMaxOffset; +} +double PieChart::getMinimumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32 /* nAxisIndex */ ) +{ + return 0.0; +} + +double PieChart::getMaximumYInRange( double /* fMinimumX */, double /* fMaximumX */, sal_Int32 /* nAxisIndex */ ) +{ + return 1.0; +} + +bool PieChart::isExpandBorderToIncrementRhythm( sal_Int32 /* nDimensionIndex */ ) +{ + return false; +} + +bool PieChart::isExpandIfValuesCloseToBorder( sal_Int32 /* nDimensionIndex */ ) +{ + return false; +} + +bool PieChart::isExpandWideValuesToZero( sal_Int32 /* nDimensionIndex */ ) +{ + return false; +} + +bool PieChart::isExpandNarrowValuesTowardZero( sal_Int32 /* nDimensionIndex */ ) +{ + return false; +} + +bool PieChart::isSeperateStackingForDifferentSigns( sal_Int32 /* nDimensionIndex */ ) +{ + return false; +} + +void PieChart::createShapes() +{ + if( m_aZSlots.begin() == m_aZSlots.end() ) //no series + return; + + DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"PieChart is not proper initialized"); + if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is())) + return; + + //the text labels should be always on top of the other series shapes + //therefore create an own group for the texts to move them to front + //(because the text group is created after the series group the texts are displayed on top) + uno::Reference< drawing::XShapes > xSeriesTarget( + createGroupShape( m_xLogicTarget,rtl::OUString() )); + uno::Reference< drawing::XShapes > xTextTarget( + m_pShapeFactory->createGroup2D( m_xFinalTarget,rtl::OUString() )); + //--------------------------------------------- + //check necessary here that different Y axis can not be stacked in the same group? ... hm? + +//============================================================================= + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = m_aZSlots[0].begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = m_aZSlots[0].end(); + + ::std::vector< VDataSeriesGroup >::size_type nExplodeableSlot = 0; + if( m_pPosHelper->isMathematicalOrientationRadius() && m_bUseRings ) + nExplodeableSlot = m_aZSlots[0].size()-1; + + m_aLabelInfoList.clear(); + ::rtl::math::setNan(&m_fMaxOffset); + +//============================================================================= + for( double fSlotX=0; aXSlotIter != aXSlotEnd && (m_bUseRings||fSlotX<0.5 ); aXSlotIter++, fSlotX+=1.0 ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + if( pSeriesList->size()<=0 )//there should be only one series in each x slot + continue; + VDataSeries* pSeries = (*pSeriesList)[0]; + if(!pSeries) + continue; + + m_pPosHelper->m_fAngleDegreeOffset = pSeries->getStartingAngle(); + + double fLogicYSum = 0.0; + //iterate through all points to get the sum + sal_Int32 nPointIndex=0; + sal_Int32 nPointCount=pSeries->getTotalPointCount(); + for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ ) + { + double fY = pSeries->getYValue( nPointIndex ); + if(fY<0.0) + { + //@todo warn somehow that negative values are treated as positive + } + if( ::rtl::math::isNan(fY) ) + continue; + fLogicYSum += fabs(fY); + } + if(fLogicYSum==0.0) + continue; + double fLogicYForNextPoint = 0.0; + //iterate through all points to create shapes + for( nPointIndex = 0; nPointIndex < nPointCount; nPointIndex++ ) + { + double fLogicInnerRadius, fLogicOuterRadius; + double fOffset = getMaxOffset(); + bool bIsVisible = m_pPosHelper->getInnerAndOuterRadius( fSlotX+1.0, fLogicInnerRadius, fLogicOuterRadius, m_bUseRings, fOffset ); + if( !bIsVisible ) + continue; + + double fLogicZ = -0.5;//as defined + double fDepth = this->getTransformedDepth(); +//============================================================================= + + uno::Reference< drawing::XShapes > xSeriesGroupShape_Shapes = getSeriesGroupShape(pSeries, xSeriesTarget); + //collect data point information (logic coordinates, style ): + double fLogicYValue = fabs(pSeries->getYValue( nPointIndex )); + if( ::rtl::math::isNan(fLogicYValue) ) + continue; + if(fLogicYValue==0.0)//@todo: continue also if the resolution to small + continue; + double fLogicYPos = fLogicYForNextPoint; + fLogicYForNextPoint += fLogicYValue; + + uno::Reference< beans::XPropertySet > xPointProperties = pSeries->getPropertiesOfPoint( nPointIndex ); + + //iterate through all subsystems to create partial points + { + //logic values on angle axis: + double fLogicStartAngleValue = fLogicYPos/fLogicYSum; + double fLogicEndAngleValue = (fLogicYPos+fLogicYValue)/fLogicYSum; + + double fExplodePercentage=0.0; + bool bDoExplode = ( nExplodeableSlot == static_cast< ::std::vector< VDataSeriesGroup >::size_type >(fSlotX) ); + if(bDoExplode) try + { + xPointProperties->getPropertyValue( C2U( "Offset" )) >>= fExplodePercentage; + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + + //--------------------------- + //transforme to unit circle: + double fUnitCircleWidthAngleDegree = m_pPosHelper->getWidthAngleDegree( fLogicStartAngleValue, fLogicEndAngleValue ); + double fUnitCircleStartAngleDegree = m_pPosHelper->transformToAngleDegree( fLogicStartAngleValue ); + double fUnitCircleInnerRadius = m_pPosHelper->transformToRadius( fLogicInnerRadius ); + double fUnitCircleOuterRadius = m_pPosHelper->transformToRadius( fLogicOuterRadius ); + + //--------------------------- + //point color: + std::auto_ptr< tPropertyNameValueMap > apOverwritePropertiesMap(0); + { + if(!pSeries->hasPointOwnColor(nPointIndex) && m_xColorScheme.is()) + { + apOverwritePropertiesMap = std::auto_ptr< tPropertyNameValueMap >( new tPropertyNameValueMap() ); + (*apOverwritePropertiesMap)[C2U("FillColor")] = uno::makeAny( + m_xColorScheme->getColorByIndex( nPointIndex )); + } + } + + //create data point + uno::Reference<drawing::XShape> xPointShape( + createDataPoint( xSeriesGroupShape_Shapes, xPointProperties + , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree + , fUnitCircleInnerRadius, fUnitCircleOuterRadius + , fLogicZ, fDepth, fExplodePercentage, apOverwritePropertiesMap.get() ) ); + + //create label + if( pSeries->getDataPointLabelIfLabel(nPointIndex) ) + { + if( !::rtl::math::approxEqual( fExplodePercentage, 0.0 ) ) + { + double fExplodeOffset = (fUnitCircleOuterRadius-fUnitCircleInnerRadius)*fExplodePercentage; + fUnitCircleInnerRadius += fExplodeOffset; + fUnitCircleOuterRadius += fExplodeOffset; + } + + sal_Int32 nLabelPlacement = pSeries->getLabelPlacement( nPointIndex, m_xChartTypeModel, m_nDimension, m_pPosHelper->isSwapXAndY() ); + bool bMovementAllowed = ( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::AVOID_OVERLAP ); + if( bMovementAllowed ) + nLabelPlacement = ::com::sun::star::chart::DataLabelPlacement::OUTSIDE; + + LabelAlignment eAlignment(LABEL_ALIGN_CENTER); + sal_Int32 nScreenValueOffsetInRadiusDirection = 0 ; + if( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::OUTSIDE ) + nScreenValueOffsetInRadiusDirection = (3!=m_nDimension) ? 150 : 0;//todo maybe calculate this font height dependent + else if( nLabelPlacement == ::com::sun::star::chart::DataLabelPlacement::INSIDE ) + nScreenValueOffsetInRadiusDirection = (3!=m_nDimension) ? -150 : 0;//todo maybe calculate this font height dependent + PolarLabelPositionHelper aPolarPosHelper(m_pPosHelper,m_nDimension,m_xLogicTarget,m_pShapeFactory); + awt::Point aScreenPosition2D( + aPolarPosHelper.getLabelScreenPositionAndAlignmentForUnitCircleValues(eAlignment, nLabelPlacement + , fUnitCircleStartAngleDegree, fUnitCircleWidthAngleDegree + , fUnitCircleInnerRadius, fUnitCircleOuterRadius, 0.0, 0 )); + + PieLabelInfo aPieLabelInfo; + aPieLabelInfo.aFirstPosition = basegfx::B2IVector( aScreenPosition2D.X, aScreenPosition2D.Y ); + awt::Point aOrigin( aPolarPosHelper.transformSceneToScreenPosition( m_pPosHelper->transformUnitCircleToScene( 0.0, 0.0, 0.5 ) ) ); + aPieLabelInfo.aOrigin = basegfx::B2IVector( aOrigin.X, aOrigin.Y ); + + //add a scaling independent Offset if requested + if( nScreenValueOffsetInRadiusDirection != 0) + { + basegfx::B2IVector aDirection( aScreenPosition2D.X- aOrigin.X, aScreenPosition2D.Y- aOrigin.Y ); + aDirection.setLength(nScreenValueOffsetInRadiusDirection); + aScreenPosition2D.X += aDirection.getX(); + aScreenPosition2D.Y += aDirection.getY(); + } + + aPieLabelInfo.xTextShape = this->createDataLabel( xTextTarget, *pSeries, nPointIndex + , fLogicYValue, fLogicYSum, aScreenPosition2D, eAlignment ); + + uno::Reference< container::XChild > xChild( aPieLabelInfo.xTextShape, uno::UNO_QUERY ); + if( xChild.is() ) + aPieLabelInfo.xLabelGroupShape = uno::Reference<drawing::XShape>( xChild->getParent(), uno::UNO_QUERY ); + aPieLabelInfo.fValue = fLogicYValue; + aPieLabelInfo.bMovementAllowed = bMovementAllowed; + aPieLabelInfo.bMoved= false; + aPieLabelInfo.xTextTarget = xTextTarget; + m_aLabelInfoList.push_back(aPieLabelInfo); + } + + if(!bDoExplode) + { + ShapeFactory::setShapeName( xPointShape + , ObjectIdentifier::createPointCID( pSeries->getPointCID_Stub(), nPointIndex ) ); + } + else try + { + //enable dragging of outer segments + + double fAngle = fUnitCircleStartAngleDegree + fUnitCircleWidthAngleDegree/2.0; + double fMaxDeltaRadius = fUnitCircleOuterRadius-fUnitCircleInnerRadius; + drawing::Position3D aOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, fUnitCircleOuterRadius, fLogicZ ); + drawing::Position3D aNewOrigin = m_pPosHelper->transformUnitCircleToScene( fAngle, fUnitCircleOuterRadius + fMaxDeltaRadius, fLogicZ ); + + sal_Int32 nOffsetPercent( static_cast<sal_Int32>(fExplodePercentage * 100.0) ); + + awt::Point aMinimumPosition( PlottingPositionHelper::transformSceneToScreenPosition( + aOrigin, m_xLogicTarget, m_pShapeFactory, m_nDimension ) ); + awt::Point aMaximumPosition( PlottingPositionHelper::transformSceneToScreenPosition( + aNewOrigin, m_xLogicTarget, m_pShapeFactory, m_nDimension ) ); + + //enable draging of piesegments + rtl::OUString aPointCIDStub( ObjectIdentifier::createSeriesSubObjectStub( OBJECTTYPE_DATA_POINT + , pSeries->getSeriesParticle() + , ObjectIdentifier::getPieSegmentDragMethodServiceName() + , ObjectIdentifier::createPieSegmentDragParameterString( + nOffsetPercent, aMinimumPosition, aMaximumPosition ) + ) ); + + ShapeFactory::setShapeName( xPointShape + , ObjectIdentifier::createPointCID( aPointCIDStub, nPointIndex ) ); + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + }//next series in x slot (next y slot) + }//next category + }//next x slot +//============================================================================= +//============================================================================= +//============================================================================= + /* @todo remove series shapes if empty + //remove and delete point-group-shape if empty + if(!xSeriesGroupShape_Shapes->getCount()) + { + (*aSeriesIter)->m_xShape.set(NULL); + m_xLogicTarget->remove(xSeriesGroupShape_Shape); + } + */ + + //remove and delete series-group-shape if empty + + //... todo +} + +namespace +{ + +::basegfx::B2IRectangle lcl_getRect( const uno::Reference< drawing::XShape >& xShape ) +{ + ::basegfx::B2IRectangle aRect; + if( xShape.is() ) + aRect = BaseGFXHelper::makeRectangle(xShape->getPosition(),xShape->getSize() ); + return aRect; +} + +bool lcl_isInsidePage( const awt::Point& rPos, const awt::Size& rSize, const awt::Size& rPageSize ) +{ + if( rPos.X < 0 || rPos.Y < 0 ) + return false; + if( (rPos.X + rSize.Width) > rPageSize.Width ) + return false; + if( (rPos.Y + rSize.Height) > rPageSize.Height ) + return false; + return true; +} + +}//end anonymous namespace + +PieChart::PieLabelInfo::PieLabelInfo() + : xTextShape(0), xLabelGroupShape(0), aFirstPosition(), aOrigin(), fValue(0.0) + , bMovementAllowed(false), bMoved(false), xTextTarget(0), pPrevious(0),pNext(0) +{ +} + +bool PieChart::PieLabelInfo::moveAwayFrom( const PieChart::PieLabelInfo* pFix, const awt::Size& rPageSize, bool bMoveHalfWay, bool bMoveClockwise, bool bAlternativeMoveDirection ) +{ + //return true if the move was successful + if(!this->bMovementAllowed) + return false; + + const sal_Int32 nLabelDistanceX = rPageSize.Width/50; + const sal_Int32 nLabelDistanceY = rPageSize.Height/50; + + ::basegfx::B2IRectangle aOverlap( lcl_getRect( this->xLabelGroupShape ) ); + aOverlap.intersect( lcl_getRect( pFix->xLabelGroupShape ) ); + if( !aOverlap.isEmpty() ) + { + (void)bAlternativeMoveDirection;//todo + + basegfx::B2IVector aRadiusDirection = this->aFirstPosition - this->aOrigin; + aRadiusDirection.setLength(1.0); + basegfx::B2IVector aTangentialDirection( -aRadiusDirection.getY(), aRadiusDirection.getX() ); + bool bShiftHorizontal = abs(aTangentialDirection.getX()) > abs(aTangentialDirection.getY()); + + sal_Int32 nShift = bShiftHorizontal ? static_cast<sal_Int32>(aOverlap.getWidth()) : static_cast<sal_Int32>(aOverlap.getHeight()); + nShift += (bShiftHorizontal ? nLabelDistanceX : nLabelDistanceY); + if( bMoveHalfWay ) + nShift/=2; + if(!bMoveClockwise) + nShift*=-1; + awt::Point aOldPos( this->xLabelGroupShape->getPosition() ); + basegfx::B2IVector aNewPos = basegfx::B2IVector( aOldPos.X, aOldPos.Y ) + nShift*aTangentialDirection; + + //check whether the new position is ok + awt::Point aNewAWTPos( aNewPos.getX(), aNewPos.getY() ); + if( !lcl_isInsidePage( aNewAWTPos, this->xLabelGroupShape->getSize(), rPageSize ) ) + return false; + + this->xLabelGroupShape->setPosition( aNewAWTPos ); + this->bMoved = true; + } + return true; +} + +void PieChart::resetLabelPositionsToPreviousState() +{ + std::vector< PieLabelInfo >::iterator aIt = m_aLabelInfoList.begin(); + std::vector< PieLabelInfo >::const_iterator aEnd = m_aLabelInfoList.end(); + for( ;aIt!=aEnd; ++aIt ) + aIt->xLabelGroupShape->setPosition(aIt->aPreviousPosition); +} + +bool PieChart::detectLabelOverlapsAndMove( const awt::Size& rPageSize ) +{ + //returns true when there might be more to do + + //find borders of a group of overlapping labels + bool bOverlapFound = false; + PieLabelInfo* pStart = &(*(m_aLabelInfoList.rbegin())); + PieLabelInfo* pFirstBorder = 0; + PieLabelInfo* pSecondBorder = 0; + PieLabelInfo* pCurrent = pStart; + do + { + ::basegfx::B2IRectangle aPreviousOverlap( lcl_getRect( pCurrent->xLabelGroupShape ) ); + ::basegfx::B2IRectangle aNextOverlap( aPreviousOverlap ); + aPreviousOverlap.intersect( lcl_getRect( pCurrent->pPrevious->xLabelGroupShape ) ); + aNextOverlap.intersect( lcl_getRect( pCurrent->pNext->xLabelGroupShape ) ); + + bool bPreviousOverlap = !aPreviousOverlap.isEmpty(); + bool bNextOverlap = !aNextOverlap.isEmpty(); + if( bPreviousOverlap || bNextOverlap ) + bOverlapFound = true; + if( !bPreviousOverlap && bNextOverlap ) + { + pFirstBorder = pCurrent; + break; + } + pCurrent = pCurrent->pNext; + } + while( pCurrent != pStart ); + + if( !bOverlapFound ) + return false; + + if( pFirstBorder ) + { + pCurrent = pFirstBorder; + do + { + ::basegfx::B2IRectangle aPreviousOverlap( lcl_getRect( pCurrent->xLabelGroupShape ) ); + ::basegfx::B2IRectangle aNextOverlap( aPreviousOverlap ); + aPreviousOverlap.intersect( lcl_getRect( pCurrent->pPrevious->xLabelGroupShape ) ); + aNextOverlap.intersect( lcl_getRect( pCurrent->pNext->xLabelGroupShape ) ); + + if( !aPreviousOverlap.isEmpty() && aNextOverlap.isEmpty() ) + { + pSecondBorder = pCurrent; + break; + } + pCurrent = pCurrent->pNext; + } + while( pCurrent != pFirstBorder ); + } + + if( !pFirstBorder || !pSecondBorder ) + { + pFirstBorder = &(*(m_aLabelInfoList.rbegin())); + pSecondBorder = &(*(m_aLabelInfoList.begin())); + } + + //find center + PieLabelInfo* pCenter = pFirstBorder; + sal_Int32 nOverlapGroupCount = 1; + for( pCurrent = pFirstBorder ;pCurrent != pSecondBorder; pCurrent = pCurrent->pNext ) + nOverlapGroupCount++; + sal_Int32 nCenterPos = nOverlapGroupCount/2; + bool bSingleCenter = nOverlapGroupCount%2 != 0; + if( bSingleCenter ) + nCenterPos++; + if(nCenterPos>1) + { + pCurrent = pFirstBorder; + while( --nCenterPos ) + pCurrent = pCurrent->pNext; + pCenter = pCurrent; + } + + //remind current positions + pCurrent = pStart; + do + { + pCurrent->aPreviousPosition = pCurrent->xLabelGroupShape->getPosition(); + pCurrent = pCurrent->pNext; + } + while( pCurrent != pStart ); + + // + bool bAlternativeMoveDirection = false; + if( !tryMoveLabels( pFirstBorder, pSecondBorder, pCenter, bSingleCenter, bAlternativeMoveDirection, rPageSize ) ) + tryMoveLabels( pFirstBorder, pSecondBorder, pCenter, bSingleCenter, bAlternativeMoveDirection, rPageSize ); + return true; +} + +bool PieChart::tryMoveLabels( PieLabelInfo* pFirstBorder, PieLabelInfo* pSecondBorder + , PieLabelInfo* pCenter + , bool bSingleCenter, bool& rbAlternativeMoveDirection, const awt::Size& rPageSize ) +{ + PieLabelInfo* p1 = bSingleCenter ? pCenter->pPrevious : pCenter; + PieLabelInfo* p2 = pCenter->pNext; + //return true when successful + + bool bLabelOrderIsAntiClockWise = m_pPosHelper->isMathematicalOrientationAngle(); + + PieLabelInfo* pCurrent = 0; + for( pCurrent = p2 ;pCurrent->pPrevious != pSecondBorder; pCurrent = pCurrent->pNext ) + { + PieLabelInfo* pFix = 0; + for( pFix = p2->pPrevious ;pFix != pCurrent; pFix = pFix->pNext ) + { + if( !pCurrent->moveAwayFrom( pFix, rPageSize, !bSingleCenter && pCurrent == p2, !bLabelOrderIsAntiClockWise, rbAlternativeMoveDirection ) ) + { + if( !rbAlternativeMoveDirection ) + { + rbAlternativeMoveDirection = true; + resetLabelPositionsToPreviousState(); + return false; + } + } + } + } + for( pCurrent = p1 ;pCurrent->pNext != pFirstBorder; pCurrent = pCurrent->pPrevious ) + { + PieLabelInfo* pFix = 0; + for( pFix = p2->pNext ;pFix != pCurrent; pFix = pFix->pPrevious ) + { + if( !pCurrent->moveAwayFrom( pFix, rPageSize, false, bLabelOrderIsAntiClockWise, rbAlternativeMoveDirection ) ) + { + if( !rbAlternativeMoveDirection ) + { + rbAlternativeMoveDirection = true; + resetLabelPositionsToPreviousState(); + return false; + } + } + } + } + return true; +} + +void PieChart::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& rPageSize ) +{ + //------------------------------------------------------------------ + //check whether there are any labels that should be moved + std::vector< PieLabelInfo >::iterator aIt1 = m_aLabelInfoList.begin(); + std::vector< PieLabelInfo >::const_iterator aEnd = m_aLabelInfoList.end(); + bool bMoveableFound = false; + for( ;aIt1!=aEnd; ++aIt1 ) + { + if(aIt1->bMovementAllowed) + { + bMoveableFound = true; + break; + } + } + if(!bMoveableFound) + return; + + double fPageDiagonaleLength = sqrt( double( rPageSize.Width*rPageSize.Width + rPageSize.Height*rPageSize.Height) ); + if( ::rtl::math::approxEqual( fPageDiagonaleLength, 0.0 ) ) + return; + + //------------------------------------------------------------------ + //init next and previous + aIt1 = m_aLabelInfoList.begin(); + std::vector< PieLabelInfo >::iterator aIt2 = aIt1; + if( aIt1==aEnd )//no need to do anything when we only have one label + return; + aIt1->pPrevious = &(*(m_aLabelInfoList.rbegin())); + ++aIt2; + for( ;aIt2!=aEnd; ++aIt1, ++aIt2 ) + { + PieLabelInfo& rInfo1( *aIt1 ); + PieLabelInfo& rInfo2( *aIt2 ); + rInfo1.pNext = &rInfo2; + rInfo2.pPrevious = &rInfo1; + } + aIt1->pNext = &(*(m_aLabelInfoList.begin())); + + + //------------------------------------------------------------------ + //detect overlaps and move + sal_Int32 nMaxIterations = 50; + while( detectLabelOverlapsAndMove( rPageSize ) && nMaxIterations > 0 ) + nMaxIterations--; + + //------------------------------------------------------------------ + //create connection lines for the moved labels + aEnd = m_aLabelInfoList.end(); + VLineProperties aVLineProperties; + for( aIt1 = m_aLabelInfoList.begin(); aIt1!=aEnd; ++aIt1 ) + { + PieLabelInfo& rInfo( *aIt1 ); + if( rInfo.bMoved ) + { + sal_Int32 nX1 = rInfo.aFirstPosition.getX(); + sal_Int32 nY1 = rInfo.aFirstPosition.getY(); + sal_Int32 nX2 = nX1; + sal_Int32 nY2 = nY1; + ::basegfx::B2IRectangle aRect( lcl_getRect( rInfo.xLabelGroupShape ) ); + if( nX1 < aRect.getMinX() ) + nX2 = aRect.getMinX(); + else if( nX1 > aRect.getMaxX() ) + nX2 = aRect.getMaxX(); + + if( nY1 < aRect.getMinY() ) + nY2 = aRect.getMinY(); + else if( nY1 > aRect.getMaxY() ) + nY2 = aRect.getMaxY(); + + + //when the line is very short compared to the page size don't create one + ::basegfx::B2DVector aLength(nX1-nX2, nY1-nY2); + if( (aLength.getLength()/fPageDiagonaleLength) < 0.01 ) + continue; + + drawing::PointSequenceSequence aPoints(1); + aPoints[0].realloc(2); + aPoints[0][0].X = nX1; + aPoints[0][0].Y = nY1; + aPoints[0][1].X = nX2; + aPoints[0][1].Y = nY2; + + uno::Reference< beans::XPropertySet > xProp( rInfo.xTextShape, uno::UNO_QUERY); + if( xProp.is() ) + { + sal_Int32 nColor = 0; + xProp->getPropertyValue(C2U("CharColor")) >>= nColor; + if( nColor != -1 )//automatic font color does not work for lines -> fallback to black + aVLineProperties.Color = uno::makeAny(nColor); + } + m_pShapeFactory->createLine2D( rInfo.xTextTarget, aPoints, &aVLineProperties ); + } + } +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/PieChart.hxx b/chart2/source/view/charttypes/PieChart.hxx new file mode 100644 index 000000000000..3f2443732899 --- /dev/null +++ b/chart2/source/view/charttypes/PieChart.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_PIECHART_HXX +#define _CHART2_PIECHART_HXX + +#include "VSeriesPlotter.hxx" +#include <basegfx/vector/b2dvector.hxx> +#include <basegfx/range/b2irectangle.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +class PiePositionHelper; + +class PieChart : public VSeriesPlotter +{ + //------------------------------------------------------------------------- + // public methods + //------------------------------------------------------------------------- +public: + PieChart( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XChartType >& xChartTypeModel + , sal_Int32 nDimensionCount, bool bExcludingPositioning ); + virtual ~PieChart(); + + //------------------------------------------------------------------------- + // chart2::XPlotter + //------------------------------------------------------------------------- + + virtual void SAL_CALL createShapes(); + virtual void rearrangeLabelToAvoidOverlapIfRequested( const ::com::sun::star::awt::Size& rPageSize ); + + virtual void SAL_CALL setScales( + const ::com::sun::star::uno::Sequence< + ::com::sun::star::chart2::ExplicitScaleData >& rScales + , sal_Bool bSwapXAndYAxis ) + throw (::com::sun::star::uno::RuntimeException); + /* + virtual ::rtl::OUString SAL_CALL getCoordinateSystemTypeID( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setScales( const ::com::sun::star::uno::Sequence< ::com::sun::star::chart2::ExplicitScaleData >& rScales ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setTransformation( const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToLogicTarget, const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToFinalPage ) throw (::com::sun::star::uno::RuntimeException); + */ + + virtual void addSeries( VDataSeries* pSeries, sal_Int32 zSlot = -1, sal_Int32 xSlot = -1,sal_Int32 ySlot = -1 ); + + //------------------- + virtual ::com::sun::star::drawing::Direction3D getPreferredDiagramAspectRatio() const; + virtual bool keepAspectRatio() const; + virtual bool shouldSnapRectToUsedArea(); + + //MinimumAndMaximumSupplier + virtual double getMinimumX(); + virtual double getMaximumX(); + virtual double getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex ); + virtual double getMaximumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex ); + + virtual bool isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex ); + virtual bool isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex ); + virtual bool isExpandWideValuesToZero( sal_Int32 nDimensionIndex ); + virtual bool isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex ); + virtual bool isSeperateStackingForDifferentSigns( sal_Int32 nDimensionIndex ); + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- +private: //methods + //no default constructor + PieChart(); + + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape > + createDataPoint( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes >& xTarget + , const ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet >& xObjectProperties + , double fUnitCircleStartAngleDegree, double fWidthAngleDegree + , double fUnitCircleInnerRadius, double fUnitCircleOuterRadius + , double fLogicZ, double fDepth, double fExplodePercentage + , tPropertyNameValueMap* pOverWritePropertiesMap ); + + double getMaxOffset(); + bool detectLabelOverlapsAndMove(const ::com::sun::star::awt::Size& rPageSize);//returns true when there might be more to do + void resetLabelPositionsToPreviousState(); +struct PieLabelInfo; + bool tryMoveLabels( PieLabelInfo* pFirstBorder, PieLabelInfo* pSecondBorder + , PieLabelInfo* pCenter, bool bSingleCenter, bool& rbAlternativeMoveDirection + , const ::com::sun::star::awt::Size& rPageSize ); + +private: //member + PiePositionHelper* m_pPosHelper; + bool m_bUseRings; + bool m_bSizeExcludesLabelsAndExplodedSegments; + + struct PieLabelInfo + { + PieLabelInfo(); + bool moveAwayFrom( const PieLabelInfo* pFix, const ::com::sun::star::awt::Size& rPageSize + , bool bMoveHalfWay, bool bMoveClockwise, bool bAlternativeMoveDirection ); + + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape > xTextShape; + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShape > xLabelGroupShape; + ::basegfx::B2IVector aFirstPosition; + ::basegfx::B2IVector aOrigin; + double fValue; + bool bMovementAllowed; + bool bMoved; + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes > xTextTarget; + PieLabelInfo* pPrevious; + PieLabelInfo* pNext; + ::com::sun::star::awt::Point aPreviousPosition; + }; + + ::std::vector< PieLabelInfo > m_aLabelInfoList; + + double m_fMaxOffset; /// cached max offset value (init'ed to NaN) +}; +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/Splines.cxx b/chart2/source/view/charttypes/Splines.cxx new file mode 100644 index 000000000000..1865ab87c779 --- /dev/null +++ b/chart2/source/view/charttypes/Splines.cxx @@ -0,0 +1,547 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "Splines.hxx" +#include <rtl/math.hxx> + +#include <vector> +#include <algorithm> +#include <functional> + +// header for DBG_ASSERT +#include <tools/debug.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; + +namespace +{ + +template< typename T > +struct lcl_EqualsFirstDoubleOfPair : ::std::binary_function< ::std::pair< double, T >, ::std::pair< double, T >, bool > +{ + inline bool operator() ( const ::std::pair< double, T > & rOne, const ::std::pair< double, T > & rOther ) + { + return ( ::rtl::math::approxEqual( rOne.first, rOther.first ) ); + } +}; + +//----------------------------------------------------------------------------- + +typedef ::std::pair< double, double > tPointType; +typedef ::std::vector< tPointType > tPointVecType; +typedef tPointVecType::size_type lcl_tSizeType; + +class lcl_SplineCalculation +{ +public: + /** @descr creates an object that calculates cublic splines on construction + + @param rSortedPoints the points for which splines shall be calculated, they need to be sorted in x values + @param fY1FirstDerivation the resulting spline should have the first + derivation equal to this value at the x-value of the first point + of rSortedPoints. If fY1FirstDerivation is set to infinity, a natural + spline is calculated. + @param fYnFirstDerivation the resulting spline should have the first + derivation equal to this value at the x-value of the last point + of rSortedPoints + */ + lcl_SplineCalculation( const tPointVecType & rSortedPoints, + double fY1FirstDerivation, + double fYnFirstDerivation ); + + /** @descr this function corresponds to the function splint in [1]. + + [1] Numerical Recipies in C, 2nd edition + William H. Press, et al., + Section 3.3, page 116 + */ + double GetInterpolatedValue( double x ); + +private: + /// a copy of the points given in the CTOR + tPointVecType m_aPoints; + + /// the result of the Calculate() method + ::std::vector< double > m_aSecDerivY; + + double m_fYp1; + double m_fYpN; + + // these values are cached for performance reasons + tPointVecType::size_type m_nKLow; + tPointVecType::size_type m_nKHigh; + double m_fLastInterpolatedValue; + + /** @descr this function corresponds to the function spline in [1]. + + [1] Numerical Recipies in C, 2nd edition + William H. Press, et al., + Section 3.3, page 115 + */ + void Calculate(); +}; + +//----------------------------------------------------------------------------- + +lcl_SplineCalculation::lcl_SplineCalculation( + const tPointVecType & rSortedPoints, + double fY1FirstDerivation, + double fYnFirstDerivation ) + : m_aPoints( rSortedPoints ), + m_fYp1( fY1FirstDerivation ), + m_fYpN( fYnFirstDerivation ), + m_nKLow( 0 ), + m_nKHigh( rSortedPoints.size() - 1 ) +{ + ::rtl::math::setInf( &m_fLastInterpolatedValue, sal_False ); + + // #108301# remove points that have equal x-values + m_aPoints.erase( ::std::unique( m_aPoints.begin(), m_aPoints.end(), + lcl_EqualsFirstDoubleOfPair< double >() ), + m_aPoints.end() ); + Calculate(); +} + +void lcl_SplineCalculation::Calculate() +{ + if( m_aPoints.size() <= 1 ) + return; + + // n is the last valid index to m_aPoints + const tPointVecType::size_type n = m_aPoints.size() - 1; + ::std::vector< double > u( n ); + m_aSecDerivY.resize( n + 1, 0.0 ); + + if( ::rtl::math::isInf( m_fYp1 ) ) + { + // natural spline + m_aSecDerivY[ 0 ] = 0.0; + u[ 0 ] = 0.0; + } + else + { + m_aSecDerivY[ 0 ] = -0.5; + double xDiff = ( m_aPoints[ 1 ].first - m_aPoints[ 0 ].first ); + u[ 0 ] = ( 3.0 / xDiff ) * + ((( m_aPoints[ 1 ].second - m_aPoints[ 0 ].second ) / xDiff ) - m_fYp1 ); + } + + for( tPointVecType::size_type i = 1; i < n; ++i ) + { + ::std::pair< double, double > + p_i = m_aPoints[ i ], + p_im1 = m_aPoints[ i - 1 ], + p_ip1 = m_aPoints[ i + 1 ]; + + double sig = ( p_i.first - p_im1.first ) / + ( p_ip1.first - p_im1.first ); + double p = sig * m_aSecDerivY[ i - 1 ] + 2.0; + + m_aSecDerivY[ i ] = ( sig - 1.0 ) / p; + u[ i ] = + ( ( p_ip1.second - p_i.second ) / + ( p_ip1.first - p_i.first ) ) - + ( ( p_i.second - p_im1.second ) / + ( p_i.first - p_im1.first ) ); + u[ i ] = + ( 6.0 * u[ i ] / ( p_ip1.first - p_im1.first ) + - sig * u[ i - 1 ] ) / p; + } + + // initialize to values for natural splines (used for m_fYpN equal to + // infinity) + double qn = 0.0; + double un = 0.0; + + if( ! ::rtl::math::isInf( m_fYpN ) ) + { + qn = 0.5; + double xDiff = ( m_aPoints[ n ].first - m_aPoints[ n - 1 ].first ); + un = ( 3.0 / xDiff ) * + ( m_fYpN - ( m_aPoints[ n ].second - m_aPoints[ n - 1 ].second ) / xDiff ); + } + + m_aSecDerivY[ n ] = ( un - qn * u[ n - 1 ] ) * ( qn * m_aSecDerivY[ n - 1 ] + 1.0 ); + + // note: the algorithm in [1] iterates from n-1 to 0, but as size_type + // may be (usuall is) an unsigned type, we can not write k >= 0, as this + // is always true. + for( tPointVecType::size_type k = n; k > 0; --k ) + { + ( m_aSecDerivY[ k - 1 ] *= m_aSecDerivY[ k ] ) += u[ k - 1 ]; + } +} + +double lcl_SplineCalculation::GetInterpolatedValue( double x ) +{ + DBG_ASSERT( ( m_aPoints[ 0 ].first <= x ) && + ( x <= m_aPoints[ m_aPoints.size() - 1 ].first ), + "Trying to extrapolate" ); + + const tPointVecType::size_type n = m_aPoints.size() - 1; + if( x < m_fLastInterpolatedValue ) + { + m_nKLow = 0; + m_nKHigh = n; + + // calculate m_nKLow and m_nKHigh + // first initialization is done in CTOR + while( m_nKHigh - m_nKLow > 1 ) + { + tPointVecType::size_type k = ( m_nKHigh + m_nKLow ) / 2; + if( m_aPoints[ k ].first > x ) + m_nKHigh = k; + else + m_nKLow = k; + } + } + else + { + while( ( m_aPoints[ m_nKHigh ].first < x ) && + ( m_nKHigh <= n ) ) + { + ++m_nKHigh; + ++m_nKLow; + } + DBG_ASSERT( m_nKHigh <= n, "Out of Bounds" ); + } + m_fLastInterpolatedValue = x; + + double h = m_aPoints[ m_nKHigh ].first - m_aPoints[ m_nKLow ].first; + DBG_ASSERT( h != 0, "Bad input to GetInterpolatedValue()" ); + + double a = ( m_aPoints[ m_nKHigh ].first - x ) / h; + double b = ( x - m_aPoints[ m_nKLow ].first ) / h; + + return ( a * m_aPoints[ m_nKLow ].second + + b * m_aPoints[ m_nKHigh ].second + + (( a*a*a - a ) * m_aSecDerivY[ m_nKLow ] + + ( b*b*b - b ) * m_aSecDerivY[ m_nKHigh ] ) * + ( h*h ) / 6.0 ); +} + +//----------------------------------------------------------------------------- + +//create knot vector for B-spline +double* createTVector( sal_Int32 n, sal_Int32 k ) +{ + double* t = new double [n + k + 1]; + for (sal_Int32 i=0; i<=n+k; i++ ) + { + if(i < k) + t[i] = 0; + else if(i <= n) + t[i] = i-k+1; + else + t[i] = n-k+2; + } + return t; +} + +//calculate left knot vector +double TLeft (double x, sal_Int32 i, sal_Int32 k, const double *t ) +{ + double deltaT = t[i + k - 1] - t[i]; + return (deltaT == 0.0) + ? 0.0 + : (x - t[i]) / deltaT; +} + +//calculate right knot vector +double TRight(double x, sal_Int32 i, sal_Int32 k, const double *t ) +{ + double deltaT = t[i + k] - t[i + 1]; + return (deltaT == 0.0) + ? 0.0 + : (t[i + k] - x) / deltaT; +} + +//calculate weight vector +void BVector(double x, sal_Int32 n, sal_Int32 k, double *b, const double *t) +{ + sal_Int32 i = 0; + for( i=0; i<=n+k; i++ ) + b[i]=0; + + sal_Int32 i0 = (sal_Int32)floor(x) + k - 1; + b [i0] = 1; + + for( sal_Int32 j=2; j<=k; j++ ) + for( i=0; i<=i0; i++ ) + b[i] = TLeft(x, i, j, t) * b[i] + TRight(x, i, j, t) * b [i + 1]; +} + +} // anonymous namespace + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +void SplineCalculater::CalculateCubicSplines( + const drawing::PolyPolygonShape3D& rInput + , drawing::PolyPolygonShape3D& rResult + , sal_Int32 nGranularity ) +{ + DBG_ASSERT( nGranularity > 0, "Granularity is invalid" ); + + rResult.SequenceX.realloc(0); + rResult.SequenceY.realloc(0); + rResult.SequenceZ.realloc(0); + + sal_Int32 nOuterCount = rInput.SequenceX.getLength(); + if( !nOuterCount ) + return; + + rResult.SequenceX.realloc(nOuterCount); + rResult.SequenceY.realloc(nOuterCount); + rResult.SequenceZ.realloc(nOuterCount); + + for( sal_Int32 nOuter = 0; nOuter < nOuterCount; ++nOuter ) + { + if( rInput.SequenceX[nOuter].getLength() <= 1 ) + continue; //we need at least two points + + sal_Int32 nMaxIndexPoints = rInput.SequenceX[nOuter].getLength()-1; // is >=1 + const double* pOldX = rInput.SequenceX[nOuter].getConstArray(); + const double* pOldY = rInput.SequenceY[nOuter].getConstArray(); + const double* pOldZ = rInput.SequenceZ[nOuter].getConstArray(); + + // #i13699# The curve gets a parameter and then for each coordinate a + // separate spline will be calculated using the parameter as first argument + // and the point coordinate as second argument. Therefore the points need + // not to be sorted in its x-coordinates. The parameter is sorted by + // construction. + + ::std::vector < double > aParameter(nMaxIndexPoints+1); + aParameter[0]=0.0; + for( sal_Int32 nIndex=1; nIndex<=nMaxIndexPoints; nIndex++ ) + { + // The euclidian distance leads to curve loops for functions having single extreme points + //aParameter[nIndex]=aParameter[nIndex-1]+ + //sqrt( (pOldX[nIndex]-pOldX[nIndex-1])*(pOldX[nIndex]-pOldX[nIndex-1])+ + //(pOldY[nIndex]-pOldY[nIndex-1])*(pOldY[nIndex]-pOldY[nIndex-1])+ + //(pOldZ[nIndex]-pOldZ[nIndex-1])*(pOldZ[nIndex]-pOldZ[nIndex-1])); + + // use increment of 1 instead + aParameter[nIndex]=aParameter[nIndex-1]+1; + } + // Split the calculation to X, Y and Z coordinate + tPointVecType aInputX; + aInputX.resize(nMaxIndexPoints+1); + tPointVecType aInputY; + aInputY.resize(nMaxIndexPoints+1); + tPointVecType aInputZ; + aInputZ.resize(nMaxIndexPoints+1); + for (sal_Int32 nN=0;nN<=nMaxIndexPoints; nN++ ) + { + aInputX[ nN ].first=aParameter[nN]; + aInputX[ nN ].second=pOldX[ nN ]; + aInputY[ nN ].first=aParameter[nN]; + aInputY[ nN ].second=pOldY[ nN ]; + aInputZ[ nN ].first=aParameter[nN]; + aInputZ[ nN ].second=pOldZ[ nN ]; + } + + // generate a spline for each coordinate. It holds the complete + // information to calculate each point of the curve + double fXDerivation; + double fYDerivation; + double fZDerivation; + if( pOldX[ 0 ] == pOldX[nMaxIndexPoints] && + pOldY[ 0 ] == pOldY[nMaxIndexPoints] && + pOldZ[ 0 ] == pOldZ[nMaxIndexPoints] ) + { + // #i101050# avoid a corner in closed lines, which are smoothed by spline + // This derivation are special for parameter of kind 0,1,2,3... If you + // change generating parameters (see above), then adapt derivations too.) + fXDerivation = 0.5 * (pOldX[1]-pOldX[nMaxIndexPoints-1]); + fYDerivation = 0.5 * (pOldY[1]-pOldY[nMaxIndexPoints-1]); + fZDerivation = 0.5 * (pOldZ[1]-pOldZ[nMaxIndexPoints-1]); + } + else // generate the kind "natural spline" + { + double fInfty; + ::rtl::math::setInf( &fInfty, sal_False ); + fXDerivation = fInfty; + fYDerivation = fInfty; + fZDerivation = fInfty; + } + lcl_SplineCalculation aSplineX( aInputX, fXDerivation, fXDerivation ); + lcl_SplineCalculation aSplineY( aInputY, fYDerivation, fYDerivation ); + lcl_SplineCalculation aSplineZ( aInputZ, fZDerivation, fZDerivation ); + + // fill result polygon with calculated values + rResult.SequenceX[nOuter].realloc( nMaxIndexPoints*nGranularity + 1); + rResult.SequenceY[nOuter].realloc( nMaxIndexPoints*nGranularity + 1); + rResult.SequenceZ[nOuter].realloc( nMaxIndexPoints*nGranularity + 1); + + double* pNewX = rResult.SequenceX[nOuter].getArray(); + double* pNewY = rResult.SequenceY[nOuter].getArray(); + double* pNewZ = rResult.SequenceZ[nOuter].getArray(); + + sal_Int32 nNewPointIndex = 0; // Index in result points + // needed for inner loop + double fInc; // step for intermediate points + sal_Int32 nj; // for loop + double fParam; // a intermediate parameter value + + for( sal_Int32 ni = 0; ni < nMaxIndexPoints; ni++ ) + { + // given point is surely a curve point + pNewX[nNewPointIndex] = pOldX[ni]; + pNewY[nNewPointIndex] = pOldY[ni]; + pNewZ[nNewPointIndex] = pOldZ[ni]; + nNewPointIndex++; + + // calculate intermediate points + fInc = ( aParameter[ ni+1 ] - aParameter[ni] ) / static_cast< double >( nGranularity ); + for(nj = 1; nj < nGranularity; nj++) + { + fParam = aParameter[ni] + ( fInc * static_cast< double >( nj ) ); + + pNewX[nNewPointIndex]=aSplineX.GetInterpolatedValue( fParam ); + pNewY[nNewPointIndex]=aSplineY.GetInterpolatedValue( fParam ); + pNewZ[nNewPointIndex]=aSplineZ.GetInterpolatedValue( fParam ); + nNewPointIndex++; + } + } + // add last point + pNewX[nNewPointIndex] = pOldX[nMaxIndexPoints]; + pNewY[nNewPointIndex] = pOldY[nMaxIndexPoints]; + pNewZ[nNewPointIndex] = pOldZ[nMaxIndexPoints]; + } +} + +void SplineCalculater::CalculateBSplines( + const ::com::sun::star::drawing::PolyPolygonShape3D& rInput + , ::com::sun::star::drawing::PolyPolygonShape3D& rResult + , sal_Int32 nGranularity + , sal_Int32 nDegree ) +{ + // #issue 72216# + // k is the order of the BSpline, nDegree is the degree of its polynoms + sal_Int32 k = nDegree + 1; + + rResult.SequenceX.realloc(0); + rResult.SequenceY.realloc(0); + rResult.SequenceZ.realloc(0); + + sal_Int32 nOuterCount = rInput.SequenceX.getLength(); + if( !nOuterCount ) + return; // no input + + rResult.SequenceX.realloc(nOuterCount); + rResult.SequenceY.realloc(nOuterCount); + rResult.SequenceZ.realloc(nOuterCount); + + for( sal_Int32 nOuter = 0; nOuter < nOuterCount; ++nOuter ) + { + if( rInput.SequenceX[nOuter].getLength() <= 1 ) + continue; // need at least 2 control points + + sal_Int32 n = rInput.SequenceX[nOuter].getLength()-1; // maximum index of control points + + double fCurveparam =0.0; // parameter for the curve + // 0<= fCurveparam < fMaxCurveparam + double fMaxCurveparam = 2.0+ n - k; + if (fMaxCurveparam <= 0.0) + return; // not enough control points for desired spline order + + if (nGranularity < 1) + return; //need at least 1 line for each part beween the control points + + const double* pOldX = rInput.SequenceX[nOuter].getConstArray(); + const double* pOldY = rInput.SequenceY[nOuter].getConstArray(); + const double* pOldZ = rInput.SequenceZ[nOuter].getConstArray(); + + // keep this amount of steps to go well with old version + sal_Int32 nNewSectorCount = nGranularity * n; + double fCurveStep = fMaxCurveparam/static_cast< double >(nNewSectorCount); + + double *b = new double [n + k + 1]; // values of blending functions + + const double* t = createTVector(n, k); // knot vector + + rResult.SequenceX[nOuter].realloc(nNewSectorCount+1); + rResult.SequenceY[nOuter].realloc(nNewSectorCount+1); + rResult.SequenceZ[nOuter].realloc(nNewSectorCount+1); + double* pNewX = rResult.SequenceX[nOuter].getArray(); + double* pNewY = rResult.SequenceY[nOuter].getArray(); + double* pNewZ = rResult.SequenceZ[nOuter].getArray(); + + // variables needed inside loop, when calculating one point of output + sal_Int32 nPointIndex =0; //index of given contol points + double fX=0.0; + double fY=0.0; + double fZ=0.0; //coordinates of a new BSpline point + + for(sal_Int32 nNewSector=0; nNewSector<nNewSectorCount; nNewSector++) + { // in first looping fCurveparam has value 0.0 + + // Calculate the values of the blending functions for actual curve parameter + BVector(fCurveparam, n, k, b, t); + + // output point(fCurveparam) = sum over {input point * value of blending function} + fX = 0.0; + fY = 0.0; + fZ = 0.0; + for (nPointIndex=0;nPointIndex<=n;nPointIndex++) + { + fX +=pOldX[nPointIndex]*b[nPointIndex]; + fY +=pOldY[nPointIndex]*b[nPointIndex]; + fZ +=pOldZ[nPointIndex]*b[nPointIndex]; + } + pNewX[nNewSector] = fX; + pNewY[nNewSector] = fY; + pNewZ[nNewSector] = fZ; + + fCurveparam += fCurveStep; //for next looping + } + // add last control point to BSpline curve + pNewX[nNewSectorCount] = pOldX[n]; + pNewY[nNewSectorCount] = pOldY[n]; + pNewZ[nNewSectorCount] = pOldZ[n]; + + delete[] t; + delete[] b; + } +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/Splines.hxx b/chart2/source/view/charttypes/Splines.hxx new file mode 100644 index 000000000000..0e8206fcade1 --- /dev/null +++ b/chart2/source/view/charttypes/Splines.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_SPLINECALCULATOR_HXX +#define _CHART2_SPLINECALCULATOR_HXX + +#include <com/sun/star/drawing/PolyPolygonShape3D.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class SplineCalculater +{ +public: + static void CalculateCubicSplines( + const ::com::sun::star::drawing::PolyPolygonShape3D& rPoints + , ::com::sun::star::drawing::PolyPolygonShape3D& rResult + , sal_Int32 nGranularity ); + + static void CalculateBSplines( + const ::com::sun::star::drawing::PolyPolygonShape3D& rPoints + , ::com::sun::star::drawing::PolyPolygonShape3D& rResult + , sal_Int32 nGranularity + , sal_Int32 nSplineDepth ); +}; + + +//............................................................................. +} //namespace chart +//............................................................................. +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx b/chart2/source/view/charttypes/VSeriesPlotter.cxx new file mode 100644 index 000000000000..97a3cbd86f2d --- /dev/null +++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx @@ -0,0 +1,2113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "VSeriesPlotter.hxx" +#include "ShapeFactory.hxx" +#include "chartview/ExplicitValueProvider.hxx" + +#include "CommonConverters.hxx" +#include "macros.hxx" +#include "ViewDefines.hxx" +#include "ObjectIdentifier.hxx" +#include "StatisticsHelper.hxx" +#include "PlottingPositionHelper.hxx" +#include "LabelPositionHelper.hxx" +#include "ChartTypeHelper.hxx" +#include "Clipping.hxx" +#include "servicenames_charttypes.hxx" +#include "chartview/NumberFormatterWrapper.hxx" +#include "ContainerHelper.hxx" +#include "DataSeriesHelper.hxx" +#include "RegressionCurveHelper.hxx" +#include "VLegendSymbolFactory.hxx" +#include "FormattedStringHelper.hxx" +#include "ResId.hxx" +#include "Strings.hrc" +#include "RelativePositionHelper.hxx" + +//only for creation: @todo remove if all plotter are uno components and instanciated via servicefactory +#include "BarChart.hxx" +#include "PieChart.hxx" +#include "AreaChart.hxx" +#include "CandleStickChart.hxx" +#include "BubbleChart.hxx" +// + +#include <com/sun/star/chart/ErrorBarStyle.hpp> +#include <com/sun/star/chart2/XRegressionCurveContainer.hpp> +#include <com/sun/star/container/XChild.hpp> +#include <com/sun/star/chart2/RelativePosition.hpp> +#include <editeng/unoprnms.hxx> +#include <tools/color.hxx> +// header for class OUStringBuffer +#include <rtl/ustrbuf.hxx> +#include <rtl/math.hxx> +#include <tools/debug.hxx> +#include <basegfx/vector/b2dvector.hxx> +#include <com/sun/star/util/XCloneable.hpp> + +#include <svx/unoshape.hxx> + +#include <functional> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using rtl::OUString; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +VDataSeriesGroup::CachedYValues::CachedYValues() + : m_bValuesDirty(true) + , m_fMinimumY(0.0) + , m_fMaximumY(0.0) +{ +} + +VDataSeriesGroup::VDataSeriesGroup() + : m_aSeriesVector() + , m_bMaxPointCountDirty(true) + , m_nMaxPointCount(0) + , m_aListOfCachedYValues() + +{ +} + +VDataSeriesGroup::VDataSeriesGroup( VDataSeries* pSeries ) + : m_aSeriesVector(1,pSeries) + , m_bMaxPointCountDirty(true) + , m_nMaxPointCount(0) + , m_aListOfCachedYValues() +{ +} + +VDataSeriesGroup::~VDataSeriesGroup() +{ +} + +void VDataSeriesGroup::deleteSeries() +{ + //delete all data series help objects: + ::std::vector< VDataSeries* >::const_iterator aIter = m_aSeriesVector.begin(); + const ::std::vector< VDataSeries* >::const_iterator aEnd = m_aSeriesVector.end(); + for( ; aIter != aEnd; aIter++ ) + { + delete *aIter; + } + m_aSeriesVector.clear(); +} + +void VDataSeriesGroup::addSeries( VDataSeries* pSeries ) +{ + m_aSeriesVector.push_back(pSeries); + m_bMaxPointCountDirty=true; +} + +sal_Int32 VDataSeriesGroup::getSeriesCount() const +{ + return m_aSeriesVector.size(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +VSeriesPlotter::VSeriesPlotter( const uno::Reference<XChartType>& xChartTypeModel + , sal_Int32 nDimensionCount, bool bCategoryXAxis ) + : PlotterBase( nDimensionCount ) + , m_pMainPosHelper( 0 ) + , m_xChartTypeModel(xChartTypeModel) + , m_xChartTypeModelProps( uno::Reference< beans::XPropertySet >::query( xChartTypeModel )) + , m_aZSlots() + , m_bCategoryXAxis(bCategoryXAxis) + , m_xColorScheme() + , m_pExplicitCategoriesProvider(0) + , m_bPointsWereSkipped(false) +{ + DBG_ASSERT(m_xChartTypeModel.is(),"no XChartType available in view, fallback to default values may be wrong"); +} + +VSeriesPlotter::~VSeriesPlotter() +{ + //delete all data series help objects: + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + aXSlotIter->deleteSeries(); + } + aZSlotIter->clear(); + } + m_aZSlots.clear(); + + tSecondaryPosHelperMap::iterator aPosIt = m_aSecondaryPosHelperMap.begin(); + while( aPosIt != m_aSecondaryPosHelperMap.end() ) + { + PlottingPositionHelper* pPosHelper = aPosIt->second; + if( pPosHelper ) + delete pPosHelper; + ++aPosIt; + } + m_aSecondaryPosHelperMap.clear(); + + m_aSecondaryValueScales.clear(); +} + +void VSeriesPlotter::addSeries( VDataSeries* pSeries, sal_Int32 zSlot, sal_Int32 xSlot, sal_Int32 ySlot ) +{ + //take ownership of pSeries + + DBG_ASSERT( pSeries, "series to add is NULL" ); + if(!pSeries) + return; + + if(m_bCategoryXAxis) + pSeries->setCategoryXAxis(); + + if(zSlot<0 || zSlot>=static_cast<sal_Int32>(m_aZSlots.size())) + { + //new z slot + ::std::vector< VDataSeriesGroup > aZSlot; + aZSlot.push_back( VDataSeriesGroup(pSeries) ); + m_aZSlots.push_back( aZSlot ); + } + else + { + //existing zslot + ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[zSlot]; + + if(xSlot<0 || xSlot>=static_cast<sal_Int32>(rXSlots.size())) + { + //append the series to already existing x series + rXSlots.push_back( VDataSeriesGroup(pSeries) ); + } + else + { + //x slot is already occupied + //y slot decides what to do: + + VDataSeriesGroup& rYSlots = rXSlots[xSlot]; + sal_Int32 nYSlotCount = rYSlots.getSeriesCount(); + + if( ySlot < -1 ) + { + //move all existing series in the xSlot to next slot + //@todo + OSL_ENSURE( false, "Not implemented yet"); + } + else if( ySlot == -1 || ySlot >= nYSlotCount) + { + //append the series to already existing y series + rYSlots.addSeries(pSeries); + } + else + { + //y slot is already occupied + //insert at given y and x position + + //@todo + OSL_ENSURE( false, "Not implemented yet"); + } + } + } +} + +drawing::Direction3D VSeriesPlotter::getPreferredDiagramAspectRatio() const +{ + drawing::Direction3D aRet(1.0,1.0,1.0); + drawing::Direction3D aScale( m_pPosHelper->getScaledLogicWidth() ); + aRet.DirectionZ = aScale.DirectionZ*0.2; + if(aRet.DirectionZ>1.0) + aRet.DirectionZ=1.0; + if(aRet.DirectionZ>10) + aRet.DirectionZ=10; + return aRet; +} + +bool VSeriesPlotter::keepAspectRatio() const +{ + return true; +} + +void VSeriesPlotter::releaseShapes() +{ + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + + ::std::vector< VDataSeries* >::iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + + //iterate through all series in this x slot + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + VDataSeries* pSeries( *aSeriesIter ); + pSeries->releaseShapes(); + } + } + } +} + +uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShape( VDataSeries* pDataSeries + , const uno::Reference< drawing::XShapes >& xTarget ) +{ + uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xGroupShape ); + if( !xShapes.is() ) + { + //create a group shape for this series and add to logic target: + xShapes = createGroupShape( xTarget,pDataSeries->getCID() ); + pDataSeries->m_xGroupShape = xShapes; + } + return xShapes; +} + +uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeFrontChild( VDataSeries* pDataSeries + , const uno::Reference< drawing::XShapes >& xTarget ) +{ + uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xFrontSubGroupShape ); + if(!xShapes.is()) + { + //ensure that the series group shape is already created + uno::Reference< drawing::XShapes > xSeriesShapes( this->getSeriesGroupShape( pDataSeries, xTarget ) ); + //ensure that the back child is created first + this->getSeriesGroupShapeBackChild( pDataSeries, xTarget ); + //use series group shape as parent for the new created front group shape + xShapes = createGroupShape( xSeriesShapes ); + pDataSeries->m_xFrontSubGroupShape = xShapes; + } + return xShapes; +} + +uno::Reference< drawing::XShapes > VSeriesPlotter::getSeriesGroupShapeBackChild( VDataSeries* pDataSeries + , const uno::Reference< drawing::XShapes >& xTarget ) +{ + uno::Reference< drawing::XShapes > xShapes( pDataSeries->m_xBackSubGroupShape ); + if(!xShapes.is()) + { + //ensure that the series group shape is already created + uno::Reference< drawing::XShapes > xSeriesShapes( this->getSeriesGroupShape( pDataSeries, xTarget ) ); + //use series group shape as parent for the new created back group shape + xShapes = createGroupShape( xSeriesShapes ); + pDataSeries->m_xBackSubGroupShape = xShapes; + } + return xShapes; +} + +uno::Reference< drawing::XShapes > VSeriesPlotter::getLabelsGroupShape( VDataSeries& rDataSeries + , const uno::Reference< drawing::XShapes >& xTextTarget ) +{ + //xTextTarget needs to be a 2D shape container always! + + uno::Reference< drawing::XShapes > xShapes( rDataSeries.m_xLabelsGroupShape ); + if(!xShapes.is()) + { + //create a 2D group shape for texts of this series and add to text target: + xShapes = m_pShapeFactory->createGroup2D( xTextTarget, rDataSeries.getLabelsCID() ); + rDataSeries.m_xLabelsGroupShape = xShapes; + } + return xShapes; +} + +uno::Reference< drawing::XShapes > VSeriesPlotter::getErrorBarsGroupShape( VDataSeries& rDataSeries + , const uno::Reference< drawing::XShapes >& xTarget ) +{ + uno::Reference< drawing::XShapes > xShapes( rDataSeries.m_xErrorBarsGroupShape ); + if(!xShapes.is()) + { + //create a group shape for this series and add to logic target: + xShapes = this->createGroupShape( xTarget,rDataSeries.getErrorBarsCID() ); + rDataSeries.m_xErrorBarsGroupShape = xShapes; + } + return xShapes; + +} + +OUString VSeriesPlotter::getLabelTextForValue( VDataSeries& rDataSeries + , sal_Int32 nPointIndex + , double fValue + , bool bAsPercentage ) +{ + OUString aNumber; + + if( m_apNumberFormatterWrapper.get()) + { + sal_Int32 nNumberFormatKey = 0; + if( rDataSeries.hasExplicitNumberFormat(nPointIndex,bAsPercentage) ) + nNumberFormatKey = rDataSeries.getExplicitNumberFormat(nPointIndex,bAsPercentage); + else if( bAsPercentage ) + { + sal_Int32 nPercentFormat = ExplicitValueProvider::getPercentNumberFormat( m_apNumberFormatterWrapper->getNumberFormatsSupplier() ); + if( nPercentFormat != -1 ) + nNumberFormatKey = nPercentFormat; + } + else + { + if( rDataSeries.shouldLabelNumberFormatKeyBeDetectedFromYAxis() && m_aAxesNumberFormats.hasFormat(1,rDataSeries.getAttachedAxisIndex()) ) //y-axis + nNumberFormatKey = m_aAxesNumberFormats.getFormat(1,rDataSeries.getAttachedAxisIndex()); + else + nNumberFormatKey = rDataSeries.detectNumberFormatKey( nPointIndex ); + } + if(nNumberFormatKey<0) + nNumberFormatKey=0; + + sal_Int32 nLabelCol = 0; + bool bColChanged; + aNumber = m_apNumberFormatterWrapper->getFormattedString( + nNumberFormatKey, fValue, nLabelCol, bColChanged ); + //@todo: change color of label if bColChanged is true + } + else + { + sal_Unicode cDecSeparator = '.';//@todo get this locale dependent + aNumber = ::rtl::math::doubleToUString( fValue, rtl_math_StringFormat_G /*rtl_math_StringFormat*/ + , 3/*DecPlaces*/ , cDecSeparator /*,sal_Int32 const * pGroups*/ /*,sal_Unicode cGroupSeparator*/ ,false /*bEraseTrailingDecZeros*/ ); + } + return aNumber; +} + +uno::Reference< drawing::XShape > VSeriesPlotter::createDataLabel( const uno::Reference< drawing::XShapes >& xTarget + , VDataSeries& rDataSeries + , sal_Int32 nPointIndex + , double fValue + , double fSumValue + , const awt::Point& rScreenPosition2D + , LabelAlignment eAlignment + , sal_Int32 nOffset ) +{ + uno::Reference< drawing::XShape > xTextShape; + + try + { + awt::Point aScreenPosition2D(rScreenPosition2D); + if(LABEL_ALIGN_LEFT==eAlignment) + aScreenPosition2D.X -= nOffset; + else if(LABEL_ALIGN_RIGHT==eAlignment) + aScreenPosition2D.X += nOffset; + else if(LABEL_ALIGN_TOP==eAlignment) + aScreenPosition2D.Y -= nOffset; + else if(LABEL_ALIGN_BOTTOM==eAlignment) + aScreenPosition2D.Y += nOffset; + + uno::Reference< drawing::XShapes > xTarget_( + m_pShapeFactory->createGroup2D( this->getLabelsGroupShape(rDataSeries, xTarget) + , ObjectIdentifier::createPointCID( rDataSeries.getLabelCID_Stub(),nPointIndex ) ) ); + + //check wether the label needs to be created and how: + DataPointLabel* pLabel = rDataSeries.getDataPointLabelIfLabel( nPointIndex ); + + if( !pLabel ) + return xTextShape; + + //------------------------------------------------ + //prepare legend symbol + + Reference< drawing::XShape > xSymbol; + if(pLabel->ShowLegendSymbol) + { + if( rDataSeries.isVaryColorsByPoint() ) + xSymbol.set( VSeriesPlotter::createLegendSymbolForPoint( rDataSeries, nPointIndex, xTarget_, m_xShapeFactory ) ); + else + xSymbol.set( VSeriesPlotter::createLegendSymbolForSeries( rDataSeries, xTarget_, m_xShapeFactory ) ); + + } + //prepare text + ::rtl::OUStringBuffer aText; + ::rtl::OUString aSeparator(sal_Unicode(' ')); + double fRotationDegrees = 0.0; + try + { + uno::Reference< beans::XPropertySet > xPointProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) ); + if(xPointProps.is()) + { + xPointProps->getPropertyValue( C2U( "LabelSeparator" ) ) >>= aSeparator; + xPointProps->getPropertyValue( C2U( "TextRotation" ) ) >>= fRotationDegrees; + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + bool bMultiLineLabel = aSeparator.equals(C2U("\n"));; + sal_Int32 nLineCountForSymbolsize = 0; + { + if(pLabel->ShowCategoryName) + { + if( m_pExplicitCategoriesProvider ) + { + Sequence< OUString > aCategories( m_pExplicitCategoriesProvider->getSimpleCategories() ); + if( nPointIndex >= 0 && nPointIndex < aCategories.getLength() ) + { + aText.append( aCategories[nPointIndex] ); + ++nLineCountForSymbolsize; + } + } + } + + if(pLabel->ShowNumber) + { + OUString aNumber( this->getLabelTextForValue( rDataSeries + , nPointIndex, fValue, false /*bAsPercentage*/ ) ); + if( aNumber.getLength() ) + { + if(aText.getLength()) + aText.append(aSeparator); + aText.append(aNumber); + ++nLineCountForSymbolsize; + } + } + + if(pLabel->ShowNumberInPercent) + { + if(fSumValue==0.0) + fSumValue=1.0; + fValue /= fSumValue; + if( fValue < 0 ) + fValue*=-1.0; + + OUString aPercentage( this->getLabelTextForValue( rDataSeries + , nPointIndex, fValue, true /*bAsPercentage*/ ) ); + if( aPercentage.getLength() ) + { + if(aText.getLength()) + aText.append(aSeparator); + aText.append(aPercentage); + ++nLineCountForSymbolsize; + } + } + } + //------------------------------------------------ + //prepare properties for multipropertyset-interface of shape + tNameSequence* pPropNames; + tAnySequence* pPropValues; + if( !rDataSeries.getTextLabelMultiPropertyLists( nPointIndex, pPropNames, pPropValues ) ) + return xTextShape; + LabelPositionHelper::changeTextAdjustment( *pPropValues, *pPropNames, eAlignment ); + + //------------------------------------------------ + //create text shape + xTextShape = ShapeFactory(m_xShapeFactory). + createText( xTarget_, aText.makeStringAndClear() + , *pPropNames, *pPropValues, ShapeFactory::makeTransformation( aScreenPosition2D ) ); + + if( !xTextShape.is() ) + return xTextShape; + + const awt::Point aUnrotatedTextPos( xTextShape->getPosition() ); + if( fRotationDegrees != 0.0 ) + { + const double fDegreesPi( fRotationDegrees * ( F_PI / -180.0 ) ); + uno::Reference< beans::XPropertySet > xProp( xTextShape, uno::UNO_QUERY ); + if( xProp.is() ) + xProp->setPropertyValue( C2U( "Transformation" ), ShapeFactory::makeTransformation( aScreenPosition2D, fDegreesPi ) ); + LabelPositionHelper::correctPositionForRotation( xTextShape, eAlignment, fRotationDegrees, true /*bRotateAroundCenter*/ ); + } + + if( xSymbol.is() ) + { + const awt::Point aOldTextPos( xTextShape->getPosition() ); + awt::Point aNewTextPos( aOldTextPos ); + + awt::Size aSymbolSize( xSymbol->getSize() ); + awt::Size aTextSize( xTextShape->getSize() ); + + if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 ) + nLineCountForSymbolsize = 1; + sal_Int32 nYDiff = aTextSize.Height/nLineCountForSymbolsize; + sal_Int32 nXDiff = aSymbolSize.Width * nYDiff/aSymbolSize.Height; + + // #i109336# Improve auto positioning in chart + nXDiff = nXDiff * 80 / 100; + nYDiff = nYDiff * 80 / 100; + + aSymbolSize.Width = nXDiff * 75/100; + aSymbolSize.Height = nYDiff * 75/100; + + awt::Point aSymbolPosition( aUnrotatedTextPos ); + + // #i109336# Improve auto positioning in chart + aSymbolPosition.Y += ( nYDiff / 4 ); + + if(LABEL_ALIGN_LEFT==eAlignment + || LABEL_ALIGN_LEFT_TOP==eAlignment + || LABEL_ALIGN_LEFT_BOTTOM==eAlignment) + { + aSymbolPosition.X -= nXDiff; + } + else if(LABEL_ALIGN_RIGHT==eAlignment + || LABEL_ALIGN_RIGHT_TOP==eAlignment + || LABEL_ALIGN_RIGHT_BOTTOM==eAlignment ) + { + aNewTextPos.X += nXDiff; + } + else if(LABEL_ALIGN_TOP==eAlignment + || LABEL_ALIGN_BOTTOM==eAlignment + || LABEL_ALIGN_CENTER==eAlignment ) + { + aSymbolPosition.X -= nXDiff/2; + aNewTextPos.X += nXDiff/2; + } + + xSymbol->setSize( aSymbolSize ); + xSymbol->setPosition( aSymbolPosition ); + + //set position + xTextShape->setPosition( aNewTextPos ); + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + + return xTextShape; +} + +namespace +{ +double lcl_getErrorBarLogicLength( + const uno::Sequence< double > & rData, + uno::Reference< beans::XPropertySet > xProp, + sal_Int32 nErrorBarStyle, + sal_Int32 nIndex, + bool bPositive ) +{ + double fResult; + ::rtl::math::setNan( & fResult ); + try + { + switch( nErrorBarStyle ) + { + case ::com::sun::star::chart::ErrorBarStyle::NONE: + break; + case ::com::sun::star::chart::ErrorBarStyle::VARIANCE: + fResult = StatisticsHelper::getVariance( rData ); + break; + case ::com::sun::star::chart::ErrorBarStyle::STANDARD_DEVIATION: + fResult = StatisticsHelper::getStandardDeviation( rData ); + break; + case ::com::sun::star::chart::ErrorBarStyle::RELATIVE: + { + double fPercent = 0; + if( xProp->getPropertyValue( bPositive + ? C2U("PositiveError") + : C2U("NegativeError")) >>= fPercent ) + { + if( nIndex >=0 && nIndex < rData.getLength() && + ! ::rtl::math::isNan( rData[nIndex] ) && + ! ::rtl::math::isNan( fPercent )) + { + fResult = rData[nIndex] * fPercent / 100.0; + } + } + } + break; + case ::com::sun::star::chart::ErrorBarStyle::ABSOLUTE: + xProp->getPropertyValue( bPositive + ? C2U("PositiveError") + : C2U("NegativeError")) >>= fResult; + break; + case ::com::sun::star::chart::ErrorBarStyle::ERROR_MARGIN: + { + // todo: check if this is really what's called error-margin + double fPercent = 0; + if( xProp->getPropertyValue( bPositive + ? C2U("PositiveError") + : C2U("NegativeError")) >>= fPercent ) + { + double fMaxValue; + ::rtl::math::setInf(&fMaxValue, true); + const double* pValues = rData.getConstArray(); + for(sal_Int32 i=0; i<rData.getLength(); ++i, ++pValues) + { + if(fMaxValue<*pValues) + fMaxValue=*pValues; + } + if( ::rtl::math::isFinite( fMaxValue ) && + ::rtl::math::isFinite( fPercent )) + { + fResult = fMaxValue * fPercent / 100.0; + } + } + } + break; + case ::com::sun::star::chart::ErrorBarStyle::STANDARD_ERROR: + fResult = StatisticsHelper::getStandardError( rData ); + break; + case ::com::sun::star::chart::ErrorBarStyle::FROM_DATA: + { + uno::Reference< chart2::data::XDataSource > xErrorBarData( xProp, uno::UNO_QUERY ); + if( xErrorBarData.is()) + fResult = StatisticsHelper::getErrorFromDataSource( + xErrorBarData, nIndex, bPositive /*, true */ /* y-error */ ); + } + break; + } + } + catch( uno::Exception & e ) + { + ASSERT_EXCEPTION( e ); + } + + return fResult; +} + +void lcl_AddErrorBottomLine( const drawing::Position3D& rPosition, ::basegfx::B2DVector aMainDirection + , drawing::PolyPolygonShape3D& rPoly, sal_Int32 nSequenceIndex ) +{ + double fFixedWidth = 200.0; + + aMainDirection.normalize(); + ::basegfx::B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX()); + aOrthoDirection.normalize(); + + ::basegfx::B2DVector aAnchor( rPosition.PositionX, rPosition.PositionY ); + ::basegfx::B2DVector aStart = aAnchor + aOrthoDirection*fFixedWidth/2.0; + ::basegfx::B2DVector aEnd = aAnchor - aOrthoDirection*fFixedWidth/2.0; + + AddPointToPoly( rPoly, drawing::Position3D( aStart.getX(), aStart.getY(), rPosition.PositionZ), nSequenceIndex ); + AddPointToPoly( rPoly, drawing::Position3D( aEnd.getX(), aEnd.getY(), rPosition.PositionZ), nSequenceIndex ); +} + +::basegfx::B2DVector lcl_getErrorBarMainDirection( + const drawing::Position3D& rStart + , const drawing::Position3D& rBottomEnd + , PlottingPositionHelper* pPosHelper + , const drawing::Position3D& rUnscaledLogicPosition + , bool bYError ) +{ + ::basegfx::B2DVector aMainDirection = ::basegfx::B2DVector( rStart.PositionX - rBottomEnd.PositionX + , rStart.PositionY - rBottomEnd.PositionY ); + if( !aMainDirection.getLength() ) + { + //get logic clip values: + double MinX = pPosHelper->getLogicMinX(); + double MinY = pPosHelper->getLogicMinY(); + double MaxX = pPosHelper->getLogicMaxX(); + double MaxY = pPosHelper->getLogicMaxY(); + double fZ = pPosHelper->getLogicMinZ(); + + + if( bYError ) + { + //main direction has constant x value + MinX = rUnscaledLogicPosition.PositionX; + MaxX = rUnscaledLogicPosition.PositionX; + } + else + { + //main direction has constant y value + MinY = rUnscaledLogicPosition.PositionY; + MaxY = rUnscaledLogicPosition.PositionY; + } + + drawing::Position3D aStart = pPosHelper->transformLogicToScene( MinX, MinY, fZ, false ); + drawing::Position3D aEnd = pPosHelper->transformLogicToScene( MaxX, MaxY, fZ, false ); + + aMainDirection = ::basegfx::B2DVector( aStart.PositionX - aEnd.PositionX + , aStart.PositionY - aEnd.PositionY ); + } + if( !aMainDirection.getLength() ) + { + //@todo + } + return aMainDirection; +} + +} // anonymous namespace + +// virtual +void VSeriesPlotter::createErrorBar( + const uno::Reference< drawing::XShapes >& xTarget + , const drawing::Position3D& rUnscaledLogicPosition + , const uno::Reference< beans::XPropertySet > & xErrorBarProperties + , const VDataSeries& rVDataSeries + , sal_Int32 nIndex + , bool bYError /* = true */ + ) +{ + if( !ChartTypeHelper::isSupportingStatisticProperties( m_xChartTypeModel, m_nDimension ) ) + return; + + if( ! xErrorBarProperties.is()) + return; + + try + { + sal_Bool bShowPositive = sal_False; + sal_Bool bShowNegative = sal_False; + sal_Int32 nErrorBarStyle = ::com::sun::star::chart::ErrorBarStyle::VARIANCE; + + xErrorBarProperties->getPropertyValue( C2U( "ShowPositiveError" )) >>= bShowPositive; + xErrorBarProperties->getPropertyValue( C2U( "ShowNegativeError" )) >>= bShowNegative; + xErrorBarProperties->getPropertyValue( C2U( "ErrorBarStyle" )) >>= nErrorBarStyle; + + if(!bShowPositive && !bShowNegative) + return; + + if(nErrorBarStyle==::com::sun::star::chart::ErrorBarStyle::NONE) + return; + + drawing::Position3D aUnscaledLogicPosition(rUnscaledLogicPosition); + if(nErrorBarStyle==::com::sun::star::chart::ErrorBarStyle::STANDARD_DEVIATION) + aUnscaledLogicPosition.PositionY = rVDataSeries.getYMeanValue(); + + bool bCreateNegativeBorder = false;//make a vertical line at the negative end of the error bar + bool bCreatePositiveBorder = false;//make a vertical line at the positive end of the error bar + drawing::Position3D aMiddle(aUnscaledLogicPosition); + const double fX = aUnscaledLogicPosition.PositionX; + const double fY = aUnscaledLogicPosition.PositionY; + const double fZ = aUnscaledLogicPosition.PositionZ; + aMiddle = m_pPosHelper->transformLogicToScene( fX, fY, fZ, true ); + + drawing::Position3D aNegative(aMiddle); + drawing::Position3D aPositive(aMiddle); + + uno::Sequence< double > aData( bYError ? rVDataSeries.getAllY() : rVDataSeries.getAllX() ); + + if( bShowPositive ) + { + double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, true ); + if( ::rtl::math::isFinite( fLength ) ) + { + double fLocalX = fX; + double fLocalY = fY; + if( bYError ) + fLocalY+=fLength; + else + fLocalX+=fLength; + bCreatePositiveBorder = m_pPosHelper->isLogicVisible(fLocalX, fLocalY, fZ); + aPositive = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true ); + } + else + bShowPositive = false; + } + + if( bShowNegative ) + { + double fLength = lcl_getErrorBarLogicLength( aData, xErrorBarProperties, nErrorBarStyle, nIndex, false ); + if( ::rtl::math::isFinite( fLength ) ) + { + double fLocalX = fX; + double fLocalY = fY; + if( bYError ) + fLocalY-=fLength; + else + fLocalX-=fLength; + + bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ); + aNegative = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true ); + } + else + bShowNegative = false; + } + + if(!bShowPositive && !bShowNegative) + return; + + drawing::PolyPolygonShape3D aPoly; + + sal_Int32 nSequenceIndex=0; + if( bShowNegative ) + AddPointToPoly( aPoly, aNegative, nSequenceIndex ); + AddPointToPoly( aPoly, aMiddle, nSequenceIndex ); + if( bShowPositive ) + AddPointToPoly( aPoly, aPositive, nSequenceIndex ); + + if( bShowNegative && bCreateNegativeBorder ) + { + ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aNegative, m_pPosHelper, aUnscaledLogicPosition, bYError ); + nSequenceIndex++; + lcl_AddErrorBottomLine( aNegative, aMainDirection, aPoly, nSequenceIndex ); + } + if( bShowPositive && bCreatePositiveBorder ) + { + ::basegfx::B2DVector aMainDirection = lcl_getErrorBarMainDirection( aMiddle, aPositive, m_pPosHelper, aUnscaledLogicPosition, bYError ); + nSequenceIndex++; + lcl_AddErrorBottomLine( aPositive, aMainDirection, aPoly, nSequenceIndex ); + } + + uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( xTarget, PolyToPointSequence( aPoly) ); + this->setMappedProperties( xShape, xErrorBarProperties, PropertyMapper::getPropertyNameMapForLineProperties() ); + } + catch( uno::Exception & e ) + { + ASSERT_EXCEPTION( e ); + } + +} + +// virtual +void VSeriesPlotter::createErrorBar_Y( const drawing::Position3D& rUnscaledLogicPosition + , VDataSeries& rVDataSeries, sal_Int32 nPointIndex + , const uno::Reference< drawing::XShapes >& xTarget ) +{ + if(m_nDimension!=2) + return; + // error bars + uno::Reference< beans::XPropertySet > xErrorBarProp(rVDataSeries.getYErrorBarProperties(nPointIndex)); + if( xErrorBarProp.is()) + { + uno::Reference< drawing::XShapes > xErrorBarsGroup_Shapes( + this->getErrorBarsGroupShape(rVDataSeries, xTarget) ); + + createErrorBar( xErrorBarsGroup_Shapes + , rUnscaledLogicPosition, xErrorBarProp + , rVDataSeries, nPointIndex + , true /* bYError */ ); + } +} + +void VSeriesPlotter::createRegressionCurvesShapes( VDataSeries& rVDataSeries + , const uno::Reference< drawing::XShapes >& xTarget + , const uno::Reference< drawing::XShapes >& xEquationTarget + , bool bMaySkipPointsInRegressionCalculation ) +{ + if(m_nDimension!=2) + return; + uno::Reference< XRegressionCurveContainer > xRegressionContainer( + rVDataSeries.getModel(), uno::UNO_QUERY ); + if(!xRegressionContainer.is()) + return; + double fMinX = m_pPosHelper->getLogicMinX(); + double fMaxX = m_pPosHelper->getLogicMaxX(); + + uno::Sequence< uno::Reference< XRegressionCurve > > aCurveList = + xRegressionContainer->getRegressionCurves(); + for(sal_Int32 nN=0; nN<aCurveList.getLength(); nN++) + { + uno::Reference< XRegressionCurveCalculator > xRegressionCurveCalculator( + aCurveList[nN]->getCalculator() ); + if( ! xRegressionCurveCalculator.is()) + continue; + xRegressionCurveCalculator->recalculateRegression( rVDataSeries.getAllX(), rVDataSeries.getAllY() ); + + sal_Int32 nRegressionPointCount = 50;//@todo find a more optimal solution if more complicated curve types are introduced + drawing::PolyPolygonShape3D aRegressionPoly; + aRegressionPoly.SequenceX.realloc(1); + aRegressionPoly.SequenceY.realloc(1); + aRegressionPoly.SequenceZ.realloc(1); + aRegressionPoly.SequenceX[0].realloc(nRegressionPointCount); + aRegressionPoly.SequenceY[0].realloc(nRegressionPointCount); + aRegressionPoly.SequenceZ[0].realloc(nRegressionPointCount); + sal_Int32 nRealPointCount=0; + + uno::Sequence< chart2::ExplicitScaleData > aScaleDataSeq( m_pPosHelper->getScales()); + uno::Reference< chart2::XScaling > xScalingX; + uno::Reference< chart2::XScaling > xScalingY; + if( aScaleDataSeq.getLength() >= 2 ) + { + xScalingX.set( aScaleDataSeq[0].Scaling ); + xScalingY.set( aScaleDataSeq[1].Scaling ); + } + + uno::Sequence< geometry::RealPoint2D > aCalculatedPoints( + xRegressionCurveCalculator->getCurveValues( + fMinX, fMaxX, nRegressionPointCount, xScalingX, xScalingY, bMaySkipPointsInRegressionCalculation )); + nRegressionPointCount = aCalculatedPoints.getLength(); + for(sal_Int32 nP=0; nP<nRegressionPointCount; nP++) + { + double fLogicX = aCalculatedPoints[nP].X; + double fLogicY = aCalculatedPoints[nP].Y; + double fLogicZ = 0.0;//dummy + + m_pPosHelper->doLogicScaling( &fLogicX, &fLogicY, &fLogicZ ); + + if( !::rtl::math::isNan(fLogicX) && !::rtl::math::isInf(fLogicX) + && !::rtl::math::isNan(fLogicY) && !::rtl::math::isInf(fLogicY) + && !::rtl::math::isNan(fLogicZ) && !::rtl::math::isInf(fLogicZ) ) + { + aRegressionPoly.SequenceX[0][nRealPointCount] = fLogicX; + aRegressionPoly.SequenceY[0][nRealPointCount] = fLogicY; + nRealPointCount++; + } + } + aRegressionPoly.SequenceX[0].realloc(nRealPointCount); + aRegressionPoly.SequenceY[0].realloc(nRealPointCount); + aRegressionPoly.SequenceZ[0].realloc(nRealPointCount); + + drawing::PolyPolygonShape3D aClippedPoly; + Clipping::clipPolygonAtRectangle( aRegressionPoly, m_pPosHelper->getScaledLogicClipDoubleRect(), aClippedPoly ); + aRegressionPoly = aClippedPoly; + m_pPosHelper->transformScaledLogicToScene( aRegressionPoly ); + + awt::Point aDefaultPos; + if( aRegressionPoly.SequenceX.getLength() && aRegressionPoly.SequenceX[0].getLength() ) + { + uno::Reference< beans::XPropertySet > xCurveModelProp( aCurveList[nN], uno::UNO_QUERY ); + VLineProperties aVLineProperties; + aVLineProperties.initFromPropertySet( xCurveModelProp ); + + //create an extra group shape for each curve for selection handling + bool bAverageLine = RegressionCurveHelper::isMeanValueLine( aCurveList[nN] ); + uno::Reference< drawing::XShapes > xRegressionGroupShapes = + createGroupShape( xTarget, rVDataSeries.getDataCurveCID( nN, bAverageLine ) ); + uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( + xRegressionGroupShapes, PolyToPointSequence( aRegressionPoly ), &aVLineProperties ); + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + aDefaultPos = xShape->getPosition(); + } + + // curve equation and correlation coefficient + uno::Reference< beans::XPropertySet > xEqProp( aCurveList[nN]->getEquationProperties()); + if( xEqProp.is()) + { + createRegressionCurveEquationShapes( + rVDataSeries.getDataCurveEquationCID( nN ), + xEqProp, xEquationTarget, xRegressionCurveCalculator, + aDefaultPos ); + } + } +} + +void VSeriesPlotter::createRegressionCurveEquationShapes( + const OUString & rEquationCID, + const uno::Reference< beans::XPropertySet > & xEquationProperties, + const uno::Reference< drawing::XShapes >& xEquationTarget, + const uno::Reference< chart2::XRegressionCurveCalculator > & xRegressionCurveCalculator, + awt::Point aDefaultPos ) +{ + OSL_ASSERT( xEquationProperties.is()); + if( !xEquationProperties.is()) + return; + + bool bShowEquation = false; + bool bShowCorrCoeff = false; + OUString aSep( sal_Unicode('\n')); + if(( xEquationProperties->getPropertyValue( C2U("ShowEquation")) >>= bShowEquation ) && + ( xEquationProperties->getPropertyValue( C2U("ShowCorrelationCoefficient")) >>= bShowCorrCoeff )) + { + if( ! (bShowEquation || bShowCorrCoeff)) + return; + + ::rtl::OUStringBuffer aFormula; + sal_Int32 nNumberFormatKey = 0; + xEquationProperties->getPropertyValue( C2U("NumberFormat")) >>= nNumberFormatKey; + + if( bShowEquation ) + { + if( m_apNumberFormatterWrapper.get()) + { + aFormula = xRegressionCurveCalculator->getFormattedRepresentation( + m_apNumberFormatterWrapper->getNumberFormatsSupplier(), + nNumberFormatKey ); + } + else + { + aFormula = xRegressionCurveCalculator->getRepresentation(); + } + + if( bShowCorrCoeff ) + { +// xEquationProperties->getPropertyValue( C2U("Separator")) >>= aSep; + aFormula.append( aSep ); + } + } + if( bShowCorrCoeff ) + { + aFormula.append( sal_Unicode( 'R' )); + aFormula.append( sal_Unicode( 0x00b2 )); + aFormula.append( C2U( " = " )); + double fR( xRegressionCurveCalculator->getCorrelationCoefficient()); + if( m_apNumberFormatterWrapper.get()) + { + sal_Int32 nLabelCol = 0; + bool bColChanged; + aFormula.append( + m_apNumberFormatterWrapper->getFormattedString( + nNumberFormatKey, fR*fR, nLabelCol, bColChanged )); + //@todo: change color of label if bColChanged is true + } + else + { + sal_Unicode aDecimalSep( '.' );//@todo get this locale dependent + aFormula.append( ::rtl::math::doubleToUString( + fR*fR, rtl_math_StringFormat_G, 4, aDecimalSep, true )); + } + } + + awt::Point aScreenPosition2D; + chart2::RelativePosition aRelativePosition; + if( xEquationProperties->getPropertyValue( C2U("RelativePosition")) >>= aRelativePosition ) + { + //@todo decide wether x is primary or secondary + double fX = aRelativePosition.Primary*m_aPageReferenceSize.Width; + double fY = aRelativePosition.Secondary*m_aPageReferenceSize.Height; + aScreenPosition2D.X = static_cast< sal_Int32 >( ::rtl::math::round( fX )); + aScreenPosition2D.Y = static_cast< sal_Int32 >( ::rtl::math::round( fY )); + } + else + aScreenPosition2D = aDefaultPos; + + if( aFormula.getLength()) + { + // set fill and line properties on creation + tNameSequence aNames; + tAnySequence aValues; + PropertyMapper::getPreparedTextShapePropertyLists( xEquationProperties, aNames, aValues ); + + uno::Reference< drawing::XShape > xTextShape = m_pShapeFactory->createText( + xEquationTarget, aFormula.makeStringAndClear(), + aNames, aValues, ShapeFactory::makeTransformation( aScreenPosition2D )); + +// // adapt font sizes +// awt::Size aOldRefSize; +// if( xTitleProperties->getPropertyValue( C2U("ReferencePageSize")) >>= aOldRefSize ) +// { +// uno::Reference< beans::XPropertySet > xShapeProp( xTextShape, uno::UNO_QUERY ); +// if( xShapeProp.is()) +// RelativeSizeHelper::adaptFontSizes( xShapeProp, aOldRefSize, m_aPageReferenceSize ); +// } + + OSL_ASSERT( xTextShape.is()); + if( xTextShape.is()) + { + ShapeFactory::setShapeName( xTextShape, rEquationCID ); + xTextShape->setPosition( + RelativePositionHelper::getUpperLeftCornerOfAnchoredObject( + aScreenPosition2D, xTextShape->getSize(), aRelativePosition.Anchor )); + } + } + } +} + + +void VSeriesPlotter::setMappedProperties( + const uno::Reference< drawing::XShape >& xTargetShape + , const uno::Reference< beans::XPropertySet >& xSource + , const tPropertyNameMap& rMap + , tPropertyNameValueMap* pOverwriteMap ) +{ + uno::Reference< beans::XPropertySet > xTargetProp( xTargetShape, uno::UNO_QUERY ); + PropertyMapper::setMappedProperties(xTargetProp,xSource,rMap,pOverwriteMap); +} + +//------------------------------------------------------------------------- +// MinimumAndMaximumSupplier +//------------------------------------------------------------------------- + +double VSeriesPlotter::getMinimumX() +{ + if( m_bCategoryXAxis ) + { + double fRet = 1.0;//first category (index 0) matches with real number 1.0 + if( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->hasComplexCategories() ) + fRet -= 0.5; + return fRet; + } + + double fMinimum, fMaximum; + this->getMinimumAndMaximiumX( fMinimum, fMaximum ); + return fMinimum; +} +double VSeriesPlotter::getMaximumX() +{ + if( m_bCategoryXAxis ) + { + //return category count + double fRet = getPointCount();//first category (index 0) matches with real number 1.0 + if( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->hasComplexCategories() ) + fRet += 0.5; + return fRet; + } + + double fMinimum, fMaximum; + this->getMinimumAndMaximiumX( fMinimum, fMaximum ); + return fMaximum; +} + +double VSeriesPlotter::getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex ) +{ + if( !m_bCategoryXAxis ) + { + double fMinY, fMaxY; + this->getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex ); + return fMinY; + } + + double fMinimum, fMaximum; + ::rtl::math::setInf(&fMinimum, false); + ::rtl::math::setInf(&fMaximum, true); + for(size_t nZ =0; nZ<m_aZSlots.size();nZ++ ) + { + ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[nZ]; + for(size_t nN =0; nN<rXSlots.size();nN++ ) + { + double fLocalMinimum, fLocalMaximum; + rXSlots[nN].calculateYMinAndMaxForCategoryRange( + static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0 + , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0 + , isSeperateStackingForDifferentSigns( 1 ) + , fLocalMinimum, fLocalMaximum, nAxisIndex ); + if(fMaximum<fLocalMaximum) + fMaximum=fLocalMaximum; + if(fMinimum>fLocalMinimum) + fMinimum=fLocalMinimum; + } + } + if(::rtl::math::isInf(fMinimum)) + ::rtl::math::setNan(&fMinimum); + return fMinimum; +} + +double VSeriesPlotter::getMaximumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex ) +{ + if( !m_bCategoryXAxis ) + { + double fMinY, fMaxY; + this->getMinimumAndMaximiumYInContinuousXRange( fMinY, fMaxY, fMinimumX, fMaximumX, nAxisIndex ); + return fMaxY; + } + + double fMinimum, fMaximum; + ::rtl::math::setInf(&fMinimum, false); + ::rtl::math::setInf(&fMaximum, true); + for(size_t nZ =0; nZ<m_aZSlots.size();nZ++ ) + { + ::std::vector< VDataSeriesGroup >& rXSlots = m_aZSlots[nZ]; + for(size_t nN =0; nN<rXSlots.size();nN++ ) + { + double fLocalMinimum, fLocalMaximum; + rXSlots[nN].calculateYMinAndMaxForCategoryRange( + static_cast<sal_Int32>(fMinimumX-1.0) //first category (index 0) matches with real number 1.0 + , static_cast<sal_Int32>(fMaximumX-1.0) //first category (index 0) matches with real number 1.0 + , isSeperateStackingForDifferentSigns( 1 ) + , fLocalMinimum, fLocalMaximum, nAxisIndex ); + if(fMaximum<fLocalMaximum) + fMaximum=fLocalMaximum; + if(fMinimum>fLocalMinimum) + fMinimum=fLocalMinimum; + } + } + if(::rtl::math::isInf(fMaximum)) + ::rtl::math::setNan(&fMaximum); + return fMaximum; +} + +double VSeriesPlotter::getMinimumZ() +{ + //this is the default for all charts without a meaningfull z axis + return 0.5; +} +double VSeriesPlotter::getMaximumZ() +{ + if( 3!=m_nDimension ) + return 0.5; + return m_aZSlots.size()+0.5; +} + +namespace +{ + bool lcl_isValueAxis( sal_Int32 nDimensionIndex, bool bCategoryXAxis ) + { + // default implementation: true for Y axes, and for value X axis + if( nDimensionIndex == 0 ) + return !bCategoryXAxis; + if( nDimensionIndex == 1 ) + return true; + return false; + } +} + +bool VSeriesPlotter::isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex ) +{ + return lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis ); +} + +bool VSeriesPlotter::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex ) +{ + // do not expand axes in 3D charts + return (m_nDimension < 3) && lcl_isValueAxis( nDimensionIndex, m_bCategoryXAxis ); +} + +bool VSeriesPlotter::isExpandWideValuesToZero( sal_Int32 nDimensionIndex ) +{ + // default implementation: only for Y axis + return nDimensionIndex == 1; +} + +bool VSeriesPlotter::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex ) +{ + // default implementation: only for Y axis + return nDimensionIndex == 1; +} + +bool VSeriesPlotter::isSeperateStackingForDifferentSigns( sal_Int32 nDimensionIndex ) +{ + // default implementation: only for Y axis + return nDimensionIndex == 1; +} + +void VSeriesPlotter::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const +{ + ::rtl::math::setInf(&rfMinimum, false); + ::rtl::math::setInf(&rfMaximum, true); + + ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + double fLocalMinimum, fLocalMaximum; + aXSlotIter->getMinimumAndMaximiumX( fLocalMinimum, fLocalMaximum ); + if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinimum ) + rfMinimum = fLocalMinimum; + if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaximum ) + rfMaximum = fLocalMaximum; + } + } + if(::rtl::math::isInf(rfMinimum)) + ::rtl::math::setNan(&rfMinimum); + if(::rtl::math::isInf(rfMaximum)) + ::rtl::math::setNan(&rfMaximum); +} + +void VSeriesPlotter::getMinimumAndMaximiumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const +{ + ::rtl::math::setInf(&rfMinY, false); + ::rtl::math::setInf(&rfMaxY, true); + + ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + double fLocalMinimum, fLocalMaximum; + aXSlotIter->getMinimumAndMaximiumYInContinuousXRange( fLocalMinimum, fLocalMaximum, fMinX, fMaxX, nAxisIndex ); + if( !::rtl::math::isNan(fLocalMinimum) && fLocalMinimum< rfMinY ) + rfMinY = fLocalMinimum; + if( !::rtl::math::isNan(fLocalMaximum) && fLocalMaximum> rfMaxY ) + rfMaxY = fLocalMaximum; + } + } + if(::rtl::math::isInf(rfMinY)) + ::rtl::math::setNan(&rfMinY); + if(::rtl::math::isInf(rfMaxY)) + ::rtl::math::setNan(&rfMaxY); +} + +sal_Int32 VSeriesPlotter::getPointCount() const +{ + sal_Int32 nRet = 0; + + ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + + for( ; aZSlotIter != aZSlotEnd; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + for( ; aXSlotIter != aXSlotEnd; aXSlotIter++ ) + { + sal_Int32 nPointCount = aXSlotIter->getPointCount(); + if( nPointCount>nRet ) + nRet = nPointCount; + } + } + return nRet; +} + +void VSeriesPlotter::setNumberFormatsSupplier( + const uno::Reference< util::XNumberFormatsSupplier > & xNumFmtSupplier ) +{ + m_apNumberFormatterWrapper.reset( new NumberFormatterWrapper( xNumFmtSupplier )); +} + +void VSeriesPlotter::setColorScheme( const uno::Reference< XColorScheme >& xColorScheme ) +{ + m_xColorScheme = xColorScheme; +} + +void VSeriesPlotter::setExplicitCategoriesProvider( ExplicitCategoriesProvider* pExplicitCategoriesProvider ) +{ + m_pExplicitCategoriesProvider = pExplicitCategoriesProvider; +} + +sal_Int32 VDataSeriesGroup::getPointCount() const +{ + if(!m_bMaxPointCountDirty) + return m_nMaxPointCount; + + sal_Int32 nRet = 0; + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = m_aSeriesVector.begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end(); + + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount(); + if( nPointCount>nRet ) + nRet = nPointCount; + } + m_nMaxPointCount=nRet; + m_aListOfCachedYValues.clear(); + m_aListOfCachedYValues.resize(m_nMaxPointCount); + m_bMaxPointCountDirty=false; + return nRet; +} + +sal_Int32 VDataSeriesGroup::getAttachedAxisIndexForFirstSeries() const +{ + sal_Int32 nRet = 0; + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = m_aSeriesVector.begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end(); + + if( aSeriesIter != aSeriesEnd ) + nRet = (*aSeriesIter)->getAttachedAxisIndex(); + + return nRet; +} + +void VDataSeriesGroup::getMinimumAndMaximiumX( double& rfMinimum, double& rfMaximum ) const +{ + const ::std::vector< VDataSeries* >* pSeriesList = &this->m_aSeriesVector; + + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + + ::rtl::math::setInf(&rfMinimum, false); + ::rtl::math::setInf(&rfMaximum, true); + + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount(); + for(sal_Int32 nN=0;nN<nPointCount;nN++) + { + double fX = (*aSeriesIter)->getXValue( nN ); + if( ::rtl::math::isNan(fX) ) + continue; + if(rfMaximum<fX) + rfMaximum=fX; + if(rfMinimum>fX) + rfMinimum=fX; + } + } + if(::rtl::math::isInf(rfMinimum)) + ::rtl::math::setNan(&rfMinimum); + if(::rtl::math::isInf(rfMaximum)) + ::rtl::math::setNan(&rfMaximum); +} +void VDataSeriesGroup::getMinimumAndMaximiumYInContinuousXRange( double& rfMinY, double& rfMaxY, double fMinX, double fMaxX, sal_Int32 nAxisIndex ) const +{ + const ::std::vector< VDataSeries* >* pSeriesList = &this->m_aSeriesVector; + + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + + ::rtl::math::setInf(&rfMinY, false); + ::rtl::math::setInf(&rfMaxY, true); + + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + sal_Int32 nPointCount = (*aSeriesIter)->getTotalPointCount(); + for(sal_Int32 nN=0;nN<nPointCount;nN++) + { + if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() ) + continue; + + double fX = (*aSeriesIter)->getXValue( nN ); + if( ::rtl::math::isNan(fX) ) + continue; + if( fX < fMinX || fX > fMaxX ) + continue; + double fY = (*aSeriesIter)->getYValue( nN ); + if( ::rtl::math::isNan(fY) ) + continue; + if(rfMaxY<fY) + rfMaxY=fY; + if(rfMinY>fY) + rfMinY=fY; + } + } + if(::rtl::math::isInf(rfMinY)) + ::rtl::math::setNan(&rfMinY); + if(::rtl::math::isInf(rfMaxY)) + ::rtl::math::setNan(&rfMaxY); +} + +void VDataSeriesGroup::calculateYMinAndMaxForCategory( sal_Int32 nCategoryIndex + , bool bSeperateStackingForDifferentSigns + , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex ) +{ + ::rtl::math::setInf(&rfMinimumY, false); + ::rtl::math::setInf(&rfMaximumY, true); + + sal_Int32 nPointCount = getPointCount();//necessary to create m_aListOfCachedYValues + if(nCategoryIndex<0 || nCategoryIndex>=nPointCount || m_aSeriesVector.empty()) + return; + + CachedYValues aCachedYValues = m_aListOfCachedYValues[nCategoryIndex][nAxisIndex]; + if( !aCachedYValues.m_bValuesDirty ) + { + //return cached values + rfMinimumY = aCachedYValues.m_fMinimumY; + rfMaximumY = aCachedYValues.m_fMaximumY; + return; + } + + double fTotalSum, fPositiveSum, fNegativeSum, fFirstPositiveY, fFirstNegativeY; + ::rtl::math::setNan( &fTotalSum ); + ::rtl::math::setNan( &fPositiveSum ); + ::rtl::math::setNan( &fNegativeSum ); + ::rtl::math::setNan( &fFirstPositiveY ); + ::rtl::math::setNan( &fFirstNegativeY ); + + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = m_aSeriesVector.begin(); + ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = m_aSeriesVector.end(); + + if( bSeperateStackingForDifferentSigns ) + { + for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter ) + { + if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() ) + continue; + + double fValueMinY = (*aSeriesIter)->getMinimumofAllDifferentYValues( nCategoryIndex ); + double fValueMaxY = (*aSeriesIter)->getMaximumofAllDifferentYValues( nCategoryIndex ); + + if( fValueMaxY >= 0 ) + { + if( ::rtl::math::isNan( fPositiveSum ) ) + fPositiveSum = fFirstPositiveY = fValueMaxY; + else + fPositiveSum += fValueMaxY; + } + if( fValueMinY < 0 ) + { + if(::rtl::math::isNan( fNegativeSum )) + fNegativeSum = fFirstNegativeY = fValueMinY; + else + fNegativeSum += fValueMinY; + } + } + rfMinimumY = ::rtl::math::isNan( fNegativeSum ) ? fFirstPositiveY : fNegativeSum; + rfMaximumY = ::rtl::math::isNan( fPositiveSum ) ? fFirstNegativeY : fPositiveSum; + } + else + { + for( ; aSeriesIter != aSeriesEnd; ++aSeriesIter ) + { + if( nAxisIndex != (*aSeriesIter)->getAttachedAxisIndex() ) + continue; + + double fValueMinY = (*aSeriesIter)->getMinimumofAllDifferentYValues( nCategoryIndex ); + double fValueMaxY = (*aSeriesIter)->getMaximumofAllDifferentYValues( nCategoryIndex ); + + if( ::rtl::math::isNan( fTotalSum ) ) + { + rfMinimumY = fValueMinY; + rfMaximumY = fTotalSum = fValueMaxY; + } + else + { + fTotalSum += fValueMaxY; + if( rfMinimumY > fTotalSum ) + rfMinimumY = fTotalSum; + if( rfMaximumY < fTotalSum ) + rfMaximumY = fTotalSum; + } + } + } + + aCachedYValues.m_fMinimumY = rfMinimumY; + aCachedYValues.m_fMaximumY = rfMaximumY; + aCachedYValues.m_bValuesDirty = false; + m_aListOfCachedYValues[nCategoryIndex][nAxisIndex]=aCachedYValues; +} + +void VDataSeriesGroup::calculateYMinAndMaxForCategoryRange( + sal_Int32 nStartCategoryIndex, sal_Int32 nEndCategoryIndex + , bool bSeperateStackingForDifferentSigns + , double& rfMinimumY, double& rfMaximumY, sal_Int32 nAxisIndex ) +{ + //@todo maybe cache these values + ::rtl::math::setInf(&rfMinimumY, false); + ::rtl::math::setInf(&rfMaximumY, true); + + //iterate through the given categories + if(nStartCategoryIndex<0) + nStartCategoryIndex=0; + if(nEndCategoryIndex<0) + nEndCategoryIndex=0; + for( sal_Int32 nCatIndex = nStartCategoryIndex; nCatIndex <= nEndCategoryIndex; nCatIndex++ ) + { + double fMinimumY; ::rtl::math::setNan(&fMinimumY); + double fMaximumY; ::rtl::math::setNan(&fMaximumY); + + this->calculateYMinAndMaxForCategory( nCatIndex + , bSeperateStackingForDifferentSigns, fMinimumY, fMaximumY, nAxisIndex ); + + if(rfMinimumY > fMinimumY) + rfMinimumY = fMinimumY; + if(rfMaximumY < fMaximumY) + rfMaximumY = fMaximumY; + } +} + +double VSeriesPlotter::getTransformedDepth() const +{ + double MinZ = m_pMainPosHelper->getLogicMinZ(); + double MaxZ = m_pMainPosHelper->getLogicMaxZ(); + m_pMainPosHelper->doLogicScaling( 0, 0, &MinZ ); + m_pMainPosHelper->doLogicScaling( 0, 0, &MaxZ ); + return FIXED_SIZE_FOR_3D_CHART_VOLUME/(MaxZ-MinZ); +} + +void SAL_CALL VSeriesPlotter::addSecondaryValueScale( const ExplicitScaleData& rScale, sal_Int32 nAxisIndex ) + throw (uno::RuntimeException) +{ + if( nAxisIndex<1 ) + return; + + m_aSecondaryValueScales[nAxisIndex]=rScale; +} + +PlottingPositionHelper& VSeriesPlotter::getPlottingPositionHelper( sal_Int32 nAxisIndex ) const +{ + PlottingPositionHelper* pRet = 0; + if(nAxisIndex>0) + { + tSecondaryPosHelperMap::const_iterator aPosIt = m_aSecondaryPosHelperMap.find( nAxisIndex ); + if( aPosIt != m_aSecondaryPosHelperMap.end() ) + { + pRet = aPosIt->second; + } + else + { + tSecondaryValueScales::const_iterator aScaleIt = m_aSecondaryValueScales.find( nAxisIndex ); + if( aScaleIt != m_aSecondaryValueScales.end() ) + { + pRet = m_pPosHelper->createSecondaryPosHelper( aScaleIt->second ); + m_aSecondaryPosHelperMap[nAxisIndex] = pRet; + } + } + } + if( !pRet ) + { + pRet = m_pMainPosHelper; + } + return *pRet; +} + +void VSeriesPlotter::rearrangeLabelToAvoidOverlapIfRequested( const awt::Size& /*rPageSize*/ ) +{ +} + +VDataSeries* VSeriesPlotter::getFirstSeries() const +{ + ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter( m_aZSlots.begin() ); + ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd( m_aZSlots.end() ); + for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter ) + { + ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + if( aXSlotIter != aXSlotEnd ) + { + VDataSeriesGroup aSeriesGroup( *aXSlotIter ); + if( aSeriesGroup.m_aSeriesVector.size() ) + { + VDataSeries* pSeries = aSeriesGroup.m_aSeriesVector[0]; + if(pSeries) + return pSeries; + } + } + } + return 0; +} + +uno::Sequence< rtl::OUString > VSeriesPlotter::getSeriesNames() const +{ + ::std::vector< rtl::OUString > aRetVector; + + rtl::OUString aRole; + if( m_xChartTypeModel.is() ) + aRole = m_xChartTypeModel->getRoleOfSequenceForSeriesLabel(); + + ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotIter( m_aZSlots.begin() ); + ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd( m_aZSlots.end() ); + for( ; aZSlotIter != aZSlotEnd; ++aZSlotIter ) + { + ::std::vector< VDataSeriesGroup >::const_iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + + if( aXSlotIter != aXSlotEnd ) + { + VDataSeriesGroup aSeriesGroup( *aXSlotIter ); + if( aSeriesGroup.m_aSeriesVector.size() ) + { + VDataSeries* pSeries = aSeriesGroup.m_aSeriesVector[0]; + uno::Reference< XDataSeries > xSeries( pSeries ? pSeries->getModel() : 0 ); + if( xSeries.is() ) + { + rtl::OUString aSeriesName( DataSeriesHelper::getDataSeriesLabel( xSeries, aRole ) ); + aRetVector.push_back( aSeriesName ); + } + } + } + } + return ContainerHelper::ContainerToSequence( aRetVector ); +} + +namespace +{ +struct lcl_setRefSizeAtSeriesGroup : public ::std::unary_function< VDataSeriesGroup, void > +{ + lcl_setRefSizeAtSeriesGroup( awt::Size aRefSize ) : m_aRefSize( aRefSize ) {} + void operator()( VDataSeriesGroup & rGroup ) + { + ::std::vector< VDataSeries* >::iterator aIt( rGroup.m_aSeriesVector.begin()); + const ::std::vector< VDataSeries* >::iterator aEndIt( rGroup.m_aSeriesVector.end()); + for( ; aIt != aEndIt; ++aIt ) + (*aIt)->setPageReferenceSize( m_aRefSize ); + } + +private: + awt::Size m_aRefSize; +}; +} // anonymous namespace + +void VSeriesPlotter::setPageReferenceSize( const ::com::sun::star::awt::Size & rPageRefSize ) +{ + m_aPageReferenceSize = rPageRefSize; + + // set reference size also at all data series + + ::std::vector< VDataSeriesGroup > aSeriesGroups( FlattenVector( m_aZSlots )); + ::std::for_each( aSeriesGroups.begin(), aSeriesGroups.end(), + lcl_setRefSizeAtSeriesGroup( m_aPageReferenceSize )); +} + +//better performance for big data +void VSeriesPlotter::setCoordinateSystemResolution( const Sequence< sal_Int32 >& rCoordinateSystemResolution ) +{ + m_aCoordinateSystemResolution = rCoordinateSystemResolution; +} + +bool VSeriesPlotter::PointsWereSkipped() const +{ + return m_bPointsWereSkipped; +} + +bool VSeriesPlotter::WantToPlotInFrontOfAxisLine() +{ + return ChartTypeHelper::isSeriesInFrontOfAxisLine( m_xChartTypeModel ); +} + +bool VSeriesPlotter::shouldSnapRectToUsedArea() +{ + if( m_nDimension == 3 ) + return false; + return true; +} + +Sequence< ViewLegendEntry > SAL_CALL VSeriesPlotter::createLegendEntries( + LegendExpansion eLegendExpansion + , const Reference< beans::XPropertySet >& xTextProperties + , const Reference< drawing::XShapes >& xTarget + , const Reference< lang::XMultiServiceFactory >& xShapeFactory + , const Reference< uno::XComponentContext >& xContext + ) throw (uno::RuntimeException) +{ + std::vector< ViewLegendEntry > aResult; + + if( xTarget.is() ) + { + //iterate through all series + bool bBreak = false; + bool bFirstSeries = true; + ::std::vector< ::std::vector< VDataSeriesGroup > >::iterator aZSlotIter = m_aZSlots.begin(); + const ::std::vector< ::std::vector< VDataSeriesGroup > >::const_iterator aZSlotEnd = m_aZSlots.end(); + for( ; aZSlotIter!=aZSlotEnd && !bBreak; aZSlotIter++ ) + { + ::std::vector< VDataSeriesGroup >::iterator aXSlotIter = aZSlotIter->begin(); + const ::std::vector< VDataSeriesGroup >::const_iterator aXSlotEnd = aZSlotIter->end(); + for( ; aXSlotIter!=aXSlotEnd && !bBreak; aXSlotIter++ ) + { + ::std::vector< VDataSeries* >* pSeriesList = &(aXSlotIter->m_aSeriesVector); + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = pSeriesList->begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = pSeriesList->end(); + //iterate through all series in this x slot + for( ; aSeriesIter!=aSeriesEnd && !bBreak; ++aSeriesIter ) + { + VDataSeries* pSeries( *aSeriesIter ); + if(!pSeries) + continue; + + std::vector< ViewLegendEntry > aSeriesEntries( this->createLegendEntriesForSeries( + *pSeries, xTextProperties, xTarget, xShapeFactory, xContext ) ); + + //add series entries to the result now + + // use only the first series if VaryColorsByPoint is set for the first series + if( bFirstSeries && pSeries->isVaryColorsByPoint() ) + bBreak = true; + bFirstSeries = false; + + // add entries reverse if chart is stacked in y-direction and the legend is not wide. + // If the legend is wide and we have a stacked bar-chart the normal order + // is the correct one + bool bReverse = false; + if( eLegendExpansion != LegendExpansion_WIDE ) + { + StackingDirection eStackingDirection( pSeries->getStackingDirection() ); + bReverse = ( eStackingDirection == StackingDirection_Y_STACKING ); + + //todo: respect direction of axis in future + } + + if(bReverse) + aResult.insert( aResult.begin(), aSeriesEntries.begin(), aSeriesEntries.end() ); + else + aResult.insert( aResult.end(), aSeriesEntries.begin(), aSeriesEntries.end() ); + } + } + } + + //add charttype specific entries if any + { + std::vector< ViewLegendEntry > aChartTypeEntries( this->createLegendEntriesForChartType( + xTextProperties, xTarget, xShapeFactory, xContext ) ); + aResult.insert( aResult.end(), aChartTypeEntries.begin(), aChartTypeEntries.end() ); + } + } + + return ::chart::ContainerHelper::ContainerToSequence( aResult ); +} + + +LegendSymbolStyle VSeriesPlotter::getLegendSymbolStyle() +{ + return chart2::LegendSymbolStyle_BOX; +} + + +uno::Any VSeriesPlotter::getExplicitSymbol( const VDataSeries& /*rSeries*/, sal_Int32 /*nPointIndex*/ ) +{ + return uno::Any(); +} + +Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForSeries( + const VDataSeries& rSeries + , const Reference< drawing::XShapes >& xTarget + , const Reference< lang::XMultiServiceFactory >& xShapeFactory ) +{ + + LegendSymbolStyle eLegendSymbolStyle = this->getLegendSymbolStyle(); + uno::Any aExplicitSymbol( this->getExplicitSymbol( rSeries ) ); + + VLegendSymbolFactory::tPropertyType ePropType = + VLegendSymbolFactory::PROP_TYPE_FILLED_SERIES; + + // todo: maybe the property-style does not solely depend on the + // legend-symbol type + switch( eLegendSymbolStyle ) + { + case LegendSymbolStyle_HORIZONTAL_LINE: + case LegendSymbolStyle_VERTICAL_LINE: + case LegendSymbolStyle_DIAGONAL_LINE: + case LegendSymbolStyle_LINE_WITH_BOX: + case LegendSymbolStyle_LINE_WITH_SYMBOL: + ePropType = VLegendSymbolFactory::PROP_TYPE_LINE_SERIES; + break; + default: + break; + }; + Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( + xTarget, eLegendSymbolStyle, xShapeFactory + , rSeries.getPropertiesOfSeries(), ePropType, aExplicitSymbol )); + + return xShape; +} + +Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForPoint( + const VDataSeries& rSeries + , sal_Int32 nPointIndex + , const Reference< drawing::XShapes >& xTarget + , const Reference< lang::XMultiServiceFactory >& xShapeFactory ) +{ + + LegendSymbolStyle eLegendSymbolStyle = this->getLegendSymbolStyle(); + uno::Any aExplicitSymbol( this->getExplicitSymbol(rSeries,nPointIndex) ); + + VLegendSymbolFactory::tPropertyType ePropType = + VLegendSymbolFactory::PROP_TYPE_FILLED_SERIES; + + // todo: maybe the property-style does not solely depend on the + // legend-symbol type + switch( eLegendSymbolStyle ) + { + case LegendSymbolStyle_HORIZONTAL_LINE: + case LegendSymbolStyle_VERTICAL_LINE: + case LegendSymbolStyle_DIAGONAL_LINE: + case LegendSymbolStyle_LINE_WITH_BOX: + case LegendSymbolStyle_LINE_WITH_SYMBOL: + ePropType = VLegendSymbolFactory::PROP_TYPE_LINE_SERIES; + break; + default: + break; + }; + + // the default properties for the data point are the data series properties. + // If a data point has own attributes overwrite them + Reference< beans::XPropertySet > xSeriesProps( rSeries.getPropertiesOfSeries() ); + Reference< beans::XPropertySet > xPointSet( xSeriesProps ); + if( rSeries.isAttributedDataPoint( nPointIndex ) ) + xPointSet.set( rSeries.getPropertiesOfPoint( nPointIndex )); + + // if a data point has no own color use a color fom the diagram's color scheme + if( ! rSeries.hasPointOwnColor( nPointIndex )) + { + Reference< util::XCloneable > xCloneable( xPointSet,uno::UNO_QUERY ); + if( xCloneable.is() && m_xColorScheme.is() ) + { + xPointSet.set( xCloneable->createClone(), uno::UNO_QUERY ); + Reference< container::XChild > xChild( xPointSet, uno::UNO_QUERY ); + if( xChild.is()) + xChild->setParent( xSeriesProps ); + + OSL_ASSERT( xPointSet.is()); + xPointSet->setPropertyValue( + C2U("Color"), uno::makeAny( m_xColorScheme->getColorByIndex( nPointIndex ))); + } + } + + Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( + xTarget, eLegendSymbolStyle, xShapeFactory, xPointSet, ePropType, aExplicitSymbol )); + + return xShape; +} + +std::vector< ViewLegendEntry > SAL_CALL VSeriesPlotter::createLegendEntriesForSeries( + const VDataSeries& rSeries + , const Reference< beans::XPropertySet >& xTextProperties + , const Reference< drawing::XShapes >& xTarget + , const Reference< lang::XMultiServiceFactory >& xShapeFactory + , const Reference< uno::XComponentContext >& xContext + ) +{ + std::vector< ViewLegendEntry > aResult; + + if( ! ( xShapeFactory.is() && xTarget.is() && xContext.is() ) ) + return aResult; + + try + { + ViewLegendEntry aEntry; + OUString aLabelText; + bool bVaryColorsByPoint = rSeries.isVaryColorsByPoint(); + if( bVaryColorsByPoint ) + { + Sequence< OUString > aCategoryNames; + if( m_pExplicitCategoriesProvider ) + aCategoryNames = m_pExplicitCategoriesProvider->getSimpleCategories(); + + for( sal_Int32 nIdx=0; nIdx<aCategoryNames.getLength(); ++nIdx ) + { + // symbol + uno::Reference< drawing::XShapes > xSymbolGroup( ShapeFactory(xShapeFactory).createGroup2D( xTarget )); + + // create the symbol + Reference< drawing::XShape > xShape( this->createLegendSymbolForPoint( + rSeries, nIdx, xSymbolGroup, xShapeFactory ) ); + + // set CID to symbol for selection + if( xShape.is() ) + { + aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY ); + + OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_DATA_POINT, nIdx ) ); + aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) ); + OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle ); + ShapeFactory::setShapeName( xShape, aCID ); + } + + // label + aLabelText = aCategoryNames[nIdx]; + if( xShape.is() || aLabelText.getLength() ) + { + aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties ); + aResult.push_back(aEntry); + } + } + } + else + { + // symbol + uno::Reference< drawing::XShapes > xSymbolGroup( ShapeFactory(xShapeFactory).createGroup2D( xTarget )); + + // create the symbol + Reference< drawing::XShape > xShape( this->createLegendSymbolForSeries( + rSeries, xSymbolGroup, xShapeFactory ) ); + + // set CID to symbol for selection + if( xShape.is()) + { + aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY ); + + OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) ); + OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle ); + ShapeFactory::setShapeName( xShape, aCID ); + } + + // label + aLabelText = ( DataSeriesHelper::getDataSeriesLabel( rSeries.getModel(), m_xChartTypeModel.is() ? m_xChartTypeModel->getRoleOfSequenceForSeriesLabel() : C2U("values-y")) ); + aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aLabelText, xTextProperties ); + + aResult.push_back(aEntry); + } + + // regression curves + if ( 3 == m_nDimension ) // #i63016# + return aResult; + + Reference< XRegressionCurveContainer > xRegrCont( rSeries.getModel(), uno::UNO_QUERY ); + if( xRegrCont.is()) + { + Sequence< Reference< XRegressionCurve > > aCurves( xRegrCont->getRegressionCurves()); + sal_Int32 i = 0, nCount = aCurves.getLength(); + for( i=0; i<nCount; ++i ) + { + if( aCurves[i].is() && !RegressionCurveHelper::isMeanValueLine( aCurves[i] ) ) + { + //label + OUString aResStr( SchResId::getResString( STR_STATISTICS_IN_LEGEND )); + replaceParamterInString( aResStr, C2U("%REGRESSIONCURVE"), RegressionCurveHelper::getUINameForRegressionCurve( aCurves[i] )); + replaceParamterInString( aResStr, C2U("%SERIESNAME"), aLabelText ); + aEntry.aLabel = FormattedStringHelper::createFormattedStringSequence( xContext, aResStr, xTextProperties ); + + // symbol + uno::Reference< drawing::XShapes > xSymbolGroup( ShapeFactory(xShapeFactory).createGroup2D( xTarget )); + + // create the symbol + Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( + xSymbolGroup, chart2::LegendSymbolStyle_DIAGONAL_LINE, xShapeFactory, + Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ), + VLegendSymbolFactory::PROP_TYPE_LINE, uno::Any() )); + + // set CID to symbol for selection + if( xShape.is()) + { + aEntry.aSymbol = uno::Reference< drawing::XShape >( xSymbolGroup, uno::UNO_QUERY ); + + bool bAverageLine = false;//@todo find out wether this is an average line or a regression curve + ObjectType eObjectType = bAverageLine ? OBJECTTYPE_DATA_AVERAGE_LINE : OBJECTTYPE_DATA_CURVE; + OUString aChildParticle( ObjectIdentifier::createChildParticleWithIndex( eObjectType, i ) ); + aChildParticle = ObjectIdentifier::addChildParticle( aChildParticle, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_LEGEND_ENTRY, 0 ) ); + OUString aCID = ObjectIdentifier::createClassifiedIdentifierForParticles( rSeries.getSeriesParticle(), aChildParticle ); + ShapeFactory::setShapeName( xShape, aCID ); + } + + aResult.push_back(aEntry); + } + } + } + } + catch( uno::Exception & ex ) + { + ASSERT_EXCEPTION( ex ); + } + return aResult; +} + +std::vector< ViewLegendEntry > SAL_CALL VSeriesPlotter::createLegendEntriesForChartType( + const Reference< beans::XPropertySet >& /* xTextProperties */, + const Reference< drawing::XShapes >& /* xTarget */, + const Reference< lang::XMultiServiceFactory >& /* xShapeFactory */, + const Reference< uno::XComponentContext >& /* xContext */ + ) +{ + return std::vector< ViewLegendEntry >(); +} + +//static +VSeriesPlotter* VSeriesPlotter::createSeriesPlotter( + const uno::Reference<XChartType>& xChartTypeModel + , sal_Int32 nDimensionCount + , bool bExcludingPositioning ) +{ + rtl::OUString aChartType = xChartTypeModel->getChartType(); + + //@todo: in future the plotter should be instanciated via service factory + VSeriesPlotter* pRet=NULL; + if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_COLUMN ) ) + pRet = new BarChart(xChartTypeModel,nDimensionCount); + else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_BAR ) ) + pRet = new BarChart(xChartTypeModel,nDimensionCount); + else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_AREA ) ) + pRet = new AreaChart(xChartTypeModel,nDimensionCount,true); + else if( aChartType.equalsIgnoreAsciiCase( CHART2_SERVICE_NAME_CHARTTYPE_LINE ) ) + pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,true); + else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_SCATTER) ) + pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true); + else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_BUBBLE) ) + pRet = new BubbleChart(xChartTypeModel,nDimensionCount); + else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_PIE) ) + pRet = new PieChart(xChartTypeModel,nDimensionCount, bExcludingPositioning ); + else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_NET) ) + pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,true,new PolarPlottingPositionHelper(),true,false,1,drawing::Direction3D(1,1,1) ); + else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_FILLED_NET) ) + pRet = new AreaChart(xChartTypeModel,nDimensionCount,true,false,new PolarPlottingPositionHelper(),true,false,1,drawing::Direction3D(1,1,1) ); + else if( aChartType.equalsIgnoreAsciiCase(CHART2_SERVICE_NAME_CHARTTYPE_CANDLESTICK) ) + pRet = new CandleStickChart(xChartTypeModel,nDimensionCount); + else + pRet = new AreaChart(xChartTypeModel,nDimensionCount,false,true); + return pRet; +} + +//............................................................................. +} //namespace chart +//............................................................................. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/chart2/source/view/charttypes/makefile.mk b/chart2/source/view/charttypes/makefile.mk new file mode 100644 index 000000000000..b6cb14edbf14 --- /dev/null +++ b/chart2/source/view/charttypes/makefile.mk @@ -0,0 +1,55 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2000, 2010 Oracle and/or its affiliates. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# This file is part of OpenOffice.org. +# +# OpenOffice.org is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License version 3 +# only, as published by the Free Software Foundation. +# +# OpenOffice.org is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License version 3 for more details +# (a copy is included in the LICENSE file that accompanied this code). +# +# You should have received a copy of the GNU Lesser General Public License +# version 3 along with OpenOffice.org. If not, see +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ= ..$/..$/.. +PRJINC= $(PRJ)$/source +PRJNAME= chart2 +TARGET= chvtypes + +ENABLE_EXCEPTIONS= TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE: settings.mk +.INCLUDE: $(PRJ)$/chartview.pmk + +# --- export library ------------------------------------------------- + +#object files to build and link together to lib $(SLB)$/$(TARGET).lib +SLOFILES = $(SLO)$/Splines.obj \ + $(SLO)$/CategoryPositionHelper.obj \ + $(SLO)$/BarPositionHelper.obj \ + $(SLO)$/VSeriesPlotter.obj \ + $(SLO)$/BarChart.obj \ + $(SLO)$/PieChart.obj \ + $(SLO)$/AreaChart.obj \ + $(SLO)$/CandleStickChart.obj \ + $(SLO)$/BubbleChart.obj + +# --- Targets ----------------------------------------------------------------- + +.INCLUDE: target.mk |