diff options
Diffstat (limited to 'chart2/source/view/charttypes/VSeriesPlotter.cxx')
-rw-r--r-- | chart2/source/view/charttypes/VSeriesPlotter.cxx | 2256 |
1 files changed, 2256 insertions, 0 deletions
diff --git a/chart2/source/view/charttypes/VSeriesPlotter.cxx b/chart2/source/view/charttypes/VSeriesPlotter.cxx new file mode 100644 index 000000000000..69138fe0c550 --- /dev/null +++ b/chart2/source/view/charttypes/VSeriesPlotter.cxx @@ -0,0 +1,2256 @@ +/* -*- 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 "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" +#include "DateHelper.hxx" +#include "DiagramHelper.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/chart/TimeUnit.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/drawing/LineStyle.hpp> +#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_nTimeResolution(::com::sun::star::chart::TimeUnit::DAY) + , m_aNullDate(30,12,1899) + , 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) + { + if( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) + pSeries->setXValues( m_pExplicitCategoriesProvider->getOriginalCategories() ); + else + pSeries->setCategoryXAxis(); + } + else + { + if( m_pExplicitCategoriesProvider ) + pSeries->setXValuesIfNone( m_pExplicitCategoriesProvider->getOriginalCategories() ); + } + + 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_FAIL( "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_FAIL( "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 = DiagramHelper::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, 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 + + float fViewFontSize( 10.0 ); + { + uno::Reference< beans::XPropertySet > xProps( rDataSeries.getPropertiesOfPoint( nPointIndex ) ); + if( xProps.is() ) + xProps->getPropertyValue( C2U( "CharHeight" )) >>= fViewFontSize; + // pt -> 1/100th mm + fViewFontSize *= (2540.0 / 72.0); + } + Reference< drawing::XShape > xSymbol; + if(pLabel->ShowLegendSymbol) + { + sal_Int32 nSymbolHeigth = static_cast< sal_Int32 >( fViewFontSize * 0.6 ); + awt::Size aCurrentRatio = this->getPreferredLegendKeyAspectRatio(); + sal_Int32 nSymbolWidth = aCurrentRatio.Width; + if( aCurrentRatio.Height > 0 ) + { + nSymbolWidth = nSymbolHeigth* aCurrentRatio.Width/aCurrentRatio.Height; + } + awt::Size aMaxSymbolExtent( nSymbolWidth, nSymbolHeigth ); + + if( rDataSeries.isVaryColorsByPoint() ) + xSymbol.set( VSeriesPlotter::createLegendSymbolForPoint( aMaxSymbolExtent, rDataSeries, nPointIndex, xTarget_, m_xShapeFactory ) ); + else + xSymbol.set( VSeriesPlotter::createLegendSymbolForSeries( aMaxSymbolExtent, 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::Point aSymbolPosition( aUnrotatedTextPos ); + awt::Size aSymbolSize( xSymbol->getSize() ); + awt::Size aTextSize( xTextShape->getSize() ); + + sal_Int32 nXDiff = aSymbolSize.Width + static_cast< sal_Int32 >( std::max( 100.0, fViewFontSize * 0.22 ) );//minimum 1mm + if( !bMultiLineLabel || nLineCountForSymbolsize <= 0 ) + nLineCountForSymbolsize = 1; + aSymbolPosition.Y += ((aTextSize.Height/nLineCountForSymbolsize)/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->setPosition( aSymbolPosition ); + 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); + } + 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; +} + +drawing::Position3D lcl_transformMixedToScene( PlottingPositionHelper* pPosHelper + , double fX /*scaled*/, double fY /*unscaled*/, double fZ /*unscaled*/, bool bClip ) +{ + if(!pPosHelper) + return drawing::Position3D(0,0,0); + pPosHelper->doLogicScaling( 0,&fY,&fZ ); + if(bClip) + pPosHelper->clipScaledLogicValues( &fX,&fY,&fZ ); + return pPosHelper->transformScaledLogicToScene( fX, fY, fZ, false ); +} + +} // anonymous namespace + +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 */ + , double* pfScaledLogicX + ) +{ + 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; + double fScaledX = fX; + if( pfScaledLogicX ) + fScaledX = *pfScaledLogicX; + else + m_pPosHelper->doLogicScaling( &fScaledX, 0, 0 ); + + aMiddle = lcl_transformMixedToScene( m_pPosHelper, fScaledX, 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; + aPositive = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ, true ); + } + else + { + fLocalX+=fLength; + aPositive = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true ); + } + bCreatePositiveBorder = m_pPosHelper->isLogicVisible(fLocalX, fLocalY, fZ); + } + 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; + aNegative = lcl_transformMixedToScene( m_pPosHelper, fScaledX, fLocalY, fZ, true ); + } + else + { + fLocalX-=fLength; + aNegative = m_pPosHelper->transformLogicToScene( fLocalX, fLocalY, fZ, true ); + } + bCreateNegativeBorder = m_pPosHelper->isLogicVisible( fLocalX, fLocalY, fZ); + } + 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 ); + } + +} + +void VSeriesPlotter::createErrorBar_Y( const drawing::Position3D& rUnscaledLogicPosition + , VDataSeries& rVDataSeries, sal_Int32 nPointIndex + , const uno::Reference< drawing::XShapes >& xTarget + , double* pfScaledLogicX ) +{ + 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 */ + , pfScaledLogicX ); + } +} + +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; + + std::vector< ExplicitScaleData > aScales( m_pPosHelper->getScales()); + uno::Reference< chart2::XScaling > xScalingX; + uno::Reference< chart2::XScaling > xScalingY; + if( aScales.size() >= 2 ) + { + xScalingX.set( aScales[0].Scaling ); + xScalingY.set( aScales[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 ) + { + 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 )); + + OSL_ASSERT( xTextShape.is()); + if( xTextShape.is()) + { + ShapeFactory::setShapeName( xTextShape, rEquationCID ); + awt::Size aSize( xTextShape->getSize() ); + awt::Point aPos( RelativePositionHelper::getUpperLeftCornerOfAnchoredObject( + aScreenPosition2D, aSize, aRelativePosition.Anchor ) ); + //ensure that the equation is fully placed within the page (if possible) + if( (aPos.X + aSize.Width) > m_aPageReferenceSize.Width ) + aPos.X = m_aPageReferenceSize.Width - aSize.Width; + if( aPos.X < 0 ) + aPos.X = 0; + if( (aPos.Y + aSize.Height) > m_aPageReferenceSize.Height ) + aPos.Y = m_aPageReferenceSize.Height - aSize.Height; + if( aPos.Y < 0 ) + aPos.Y = 0; + xTextShape->setPosition(aPos); + } + } + } +} + + +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); +} + +void VSeriesPlotter::setTimeResolutionOnXAxis( long TimeResolution, const Date& rNullDate ) +{ + m_nTimeResolution = TimeResolution; + m_aNullDate = rNullDate; +} + +//------------------------------------------------------------------------- +// MinimumAndMaximumSupplier +//------------------------------------------------------------------------- +long VSeriesPlotter::calculateTimeResolutionOnXAxis() +{ + long nRet = ::com::sun::star::chart::TimeUnit::YEAR; + if( m_pExplicitCategoriesProvider ) + { + const std::vector< DatePlusIndex >& rDateCategories = m_pExplicitCategoriesProvider->getDateCategories(); + std::vector< DatePlusIndex >::const_iterator aIt = rDateCategories.begin(), aEnd = rDateCategories.end(); + Date aNullDate(30,12,1899); + if( m_apNumberFormatterWrapper.get() ) + aNullDate = m_apNumberFormatterWrapper->getNullDate(); + if( aIt!=aEnd ) + { + Date aPrevious(aNullDate); aPrevious+=rtl::math::approxFloor(aIt->fValue); + ++aIt; + for(;aIt!=aEnd;++aIt) + { + Date aCurrent(aNullDate); aCurrent+=rtl::math::approxFloor(aIt->fValue); + if( ::com::sun::star::chart::TimeUnit::YEAR == nRet ) + { + if( DateHelper::IsInSameYear( aPrevious, aCurrent ) ) + nRet = ::com::sun::star::chart::TimeUnit::MONTH; + } + if( ::com::sun::star::chart::TimeUnit::MONTH == nRet ) + { + if( DateHelper::IsInSameMonth( aPrevious, aCurrent ) ) + nRet = ::com::sun::star::chart::TimeUnit::DAY; + } + if( ::com::sun::star::chart::TimeUnit::DAY == nRet ) + break; + aPrevious=aCurrent; + } + } + } + return nRet; +} +double VSeriesPlotter::getMinimumX() +{ + double fMinimum, fMaximum; + this->getMinimumAndMaximiumX( fMinimum, fMaximum ); + return fMinimum; +} +double VSeriesPlotter::getMaximumX() +{ + double fMinimum, fMaximum; + this->getMinimumAndMaximiumX( fMinimum, fMaximum ); + return fMaximum; +} + +double VSeriesPlotter::getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex ) +{ + if( !m_bCategoryXAxis || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) ) + { + 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 || ( m_pExplicitCategoriesProvider && m_pExplicitCategoriesProvider->isDateAxis() ) ) + { + 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 1.0; +} +double VSeriesPlotter::getMaximumZ() +{ + if( 3!=m_nDimension || !m_aZSlots.size() ) + return getMinimumZ()+1; + return m_aZSlots.size(); +} + +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 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; + if(pRet) + pRet->setTimeResolution( m_nTimeResolution, m_aNullDate ); + 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; +} + +std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntries( + const awt::Size& rEntryKeyAspectRatio + , ::com::sun::star::chart::ChartLegendExpansion eLegendExpansion + , 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( 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( rEntryKeyAspectRatio, + *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 != ::com::sun::star::chart::ChartLegendExpansion_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() ); + } + } + } + } + + return aResult; +} + +::std::vector< VDataSeries* > VSeriesPlotter::getAllSeries() +{ + ::std::vector< VDataSeries* > aAllSeries; + ::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* > aSeriesList = aXSlotIter->m_aSeriesVector; + aAllSeries.insert( aAllSeries.end(), aSeriesList.begin(), aSeriesList.end() ); + } + } + return aAllSeries; +} + +namespace +{ +bool lcl_HasVisibleLine( const uno::Reference< beans::XPropertySet >& xProps, bool& rbHasDashedLine ) +{ + bool bHasVisibleLine = false; + rbHasDashedLine = false; + drawing::LineStyle aLineStyle = drawing::LineStyle_NONE; + if( xProps.is() && ( xProps->getPropertyValue( C2U("LineStyle")) >>= aLineStyle ) ) + { + if( aLineStyle != drawing::LineStyle_NONE ) + bHasVisibleLine = true; + if( aLineStyle == drawing::LineStyle_DASH ) + rbHasDashedLine = true; + } + return bHasVisibleLine; +} + +bool lcl_HasRegressionCurves( const VDataSeries& rSeries, bool& rbHasDashedLine ) +{ + bool bHasRegressionCurves = false; + 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() ) + { + bHasRegressionCurves = true; + lcl_HasVisibleLine( uno::Reference< beans::XPropertySet >( aCurves[i], uno::UNO_QUERY ), rbHasDashedLine ); + } + } + } + return bHasRegressionCurves; +} +} +LegendSymbolStyle VSeriesPlotter::getLegendSymbolStyle() +{ + return LegendSymbolStyle_BOX; +} + +awt::Size VSeriesPlotter::getPreferredLegendKeyAspectRatio() +{ + awt::Size aRet(1000,1000); + if( m_nDimension==3 ) + return aRet; + + bool bSeriesAllowsLines = (getLegendSymbolStyle() == LegendSymbolStyle_LINE); + bool bHasLines = false; + bool bHasDashedLines = false; + ::std::vector< VDataSeries* > aAllSeries( getAllSeries() ); + ::std::vector< VDataSeries* >::const_iterator aSeriesIter = aAllSeries.begin(); + const ::std::vector< VDataSeries* >::const_iterator aSeriesEnd = aAllSeries.end(); + //iterate through all series + for( ; aSeriesIter != aSeriesEnd; aSeriesIter++ ) + { + if( bSeriesAllowsLines ) + { + bool bCurrentDashed = false; + if( lcl_HasVisibleLine( (*aSeriesIter)->getPropertiesOfSeries(), bCurrentDashed ) ) + { + bHasLines = true; + if( bCurrentDashed ) + { + bHasDashedLines = true; + break; + } + } + } + bool bRegressionHasDashedLines=false; + if( lcl_HasRegressionCurves( **aSeriesIter, bRegressionHasDashedLines ) ) + { + bHasLines = true; + if( bRegressionHasDashedLines ) + { + bHasDashedLines = true; + break; + } + } + } + if( bHasLines ) + { + if( bHasDashedLines ) + aRet = awt::Size(1600,-1); + else + aRet = awt::Size(800,-1); + } + return aRet; +} + +uno::Any VSeriesPlotter::getExplicitSymbol( const VDataSeries& /*rSeries*/, sal_Int32 /*nPointIndex*/ ) +{ + return uno::Any(); +} + +Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForSeries( + const awt::Size& rEntryKeyAspectRatio + , 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_LINE: + ePropType = VLegendSymbolFactory::PROP_TYPE_LINE_SERIES; + break; + default: + break; + }; + Reference< drawing::XShape > xShape( VLegendSymbolFactory::createSymbol( rEntryKeyAspectRatio, + xTarget, eLegendSymbolStyle, xShapeFactory + , rSeries.getPropertiesOfSeries(), ePropType, aExplicitSymbol )); + + return xShape; +} + +Reference< drawing::XShape > VSeriesPlotter::createLegendSymbolForPoint( + const awt::Size& rEntryKeyAspectRatio + , 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_LINE: + 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( rEntryKeyAspectRatio, + xTarget, eLegendSymbolStyle, xShapeFactory, xPointSet, ePropType, aExplicitSymbol )); + + return xShape; +} + +std::vector< ViewLegendEntry > VSeriesPlotter::createLegendEntriesForSeries( + const awt::Size& rEntryKeyAspectRatio + , 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( rEntryKeyAspectRatio, + 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( + rEntryKeyAspectRatio, 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() ) + { + //label + OUString aResStr( 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( rEntryKeyAspectRatio, + xSymbolGroup, LegendSymbolStyle_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 = RegressionCurveHelper::isMeanValueLine( aCurves[i] ); + 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; +} + +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: */ |