blob: 16beedbd6daf4996ae66b0fdb00e2ce58e5032e2 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the 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_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 = 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 /*,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
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.isEmpty() )
{
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.isEmpty() )
{
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 /*, 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;
}
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
// 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 */
, 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 );
}
}
// virtual
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 )
{
// 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 );
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+=static_cast<long>(rtl::math::approxFloor(aIt->fValue));
++aIt;
for(;aIt!=aEnd;++aIt)
{
Date aCurrent(aNullDate); aCurrent+=static_cast<long>(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;
////for stockchart with candlestick start
//todo:stockchart with candlestick has no values-y but values-min,values-max,values-first and values-last
//also check values-min,values-max for max and min value
double fYmax = (*aSeriesIter)->getY_Max( nN );
double fYmin = (*aSeriesIter)->getY_Min( nN );
if(!::rtl::math::isNan(fYmax))
if( rfMaxY<fYmax) rfMaxY=fYmax;
if(!::rtl::math::isNan(fYmin))
if( rfMinY>fYmin) rfMinY=fYmin;
////for stockchart with candlestick end
}
}
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.isEmpty() )
{
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
//.............................................................................