1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <BaseGFXHelper.hxx>
21 #include <DateHelper.hxx>
22 #include <VCoordinateSystem.hxx>
23 #include "VCartesianCoordinateSystem.hxx"
24 #include "VPolarCoordinateSystem.hxx"
25 #include <BaseCoordinateSystem.hxx>
26 #include <GridProperties.hxx>
27 #include <ChartModel.hxx>
28 #include <ScaleAutomatism.hxx>
29 #include <ShapeFactory.hxx>
30 #include <servicenames_coosystems.hxx>
31 #include <ObjectIdentifier.hxx>
32 #include <ExplicitCategoriesProvider.hxx>
34 #include "VAxisBase.hxx"
35 #include <defines.hxx>
36 #include <chartview/ExplicitValueProvider.hxx>
37 #include <com/sun/star/chart/TimeUnit.hpp>
38 #include <com/sun/star/chart2/AxisType.hpp>
39 #include <rtl/math.hxx>
40 #include <comphelper/diagnose_ex.hxx>
48 using namespace ::com::sun::star
;
49 using namespace ::com::sun::star::chart2
;
50 using ::com::sun::star::uno::Reference
;
51 using ::com::sun::star::uno::Sequence
;
53 std::unique_ptr
<VCoordinateSystem
> VCoordinateSystem::createCoordinateSystem(
54 const rtl::Reference
< BaseCoordinateSystem
>& xCooSysModel
)
56 if( !xCooSysModel
.is() )
59 OUString aViewServiceName
= xCooSysModel
->getViewServiceName();
61 //@todo: in future the coordinatesystems should be instantiated via service factory
62 std::unique_ptr
<VCoordinateSystem
> pRet
;
63 if( aViewServiceName
== CHART2_COOSYSTEM_CARTESIAN_VIEW_SERVICE_NAME
)
64 pRet
.reset( new VCartesianCoordinateSystem(xCooSysModel
) );
65 else if( aViewServiceName
== CHART2_COOSYSTEM_POLAR_VIEW_SERVICE_NAME
)
66 pRet
.reset( new VPolarCoordinateSystem(xCooSysModel
) );
68 pRet
.reset( new VCoordinateSystem(xCooSysModel
) );
72 VCoordinateSystem::VCoordinateSystem( rtl::Reference
< BaseCoordinateSystem
> xCooSys
)
73 : m_xCooSysModel(std::move(xCooSys
))
74 , m_eLeftWallPos(CuboidPlanePosition_Left
)
75 , m_eBackWallPos(CuboidPlanePosition_Back
)
76 , m_eBottomPos(CuboidPlanePosition_Bottom
)
77 , m_aExplicitScales(3)
78 , m_aExplicitIncrements(3)
80 if( !m_xCooSysModel
.is() || m_xCooSysModel
->getDimension()<3 )
82 m_aExplicitScales
[2].Minimum
= 1.0;
83 m_aExplicitScales
[2].Maximum
= 2.0;
84 m_aExplicitScales
[2].Orientation
= AxisOrientation_MATHEMATICAL
;
87 VCoordinateSystem::~VCoordinateSystem()
91 void VCoordinateSystem::initPlottingTargets( const rtl::Reference
< SvxShapeGroupAnyD
>& xLogicTarget
92 , const rtl::Reference
< SvxShapeGroupAnyD
>& xFinalTarget
93 , rtl::Reference
<SvxShapeGroupAnyD
>& xLogicTargetForSeriesBehindAxis
)
95 OSL_PRECOND(xLogicTarget
.is()&&xFinalTarget
.is(),"no proper initialization parameters");
96 //is only allowed to be called once
98 sal_Int32 nDimensionCount
= m_xCooSysModel
->getDimension();
99 //create group shape for grids first thus axes are always painted above grids
100 if(nDimensionCount
==2)
102 //create and add to target
103 m_xLogicTargetForGrids
= ShapeFactory::createGroup2D( xLogicTarget
);
104 xLogicTargetForSeriesBehindAxis
= ShapeFactory::createGroup2D( xLogicTarget
);
105 m_xLogicTargetForAxes
= ShapeFactory::createGroup2D( xLogicTarget
);
109 //create and added to target
110 m_xLogicTargetForGrids
= ShapeFactory::createGroup3D( xLogicTarget
);
111 xLogicTargetForSeriesBehindAxis
= ShapeFactory::createGroup3D( xLogicTarget
);
112 m_xLogicTargetForAxes
= ShapeFactory::createGroup3D( xLogicTarget
);
114 m_xFinalTarget
= xFinalTarget
;
117 void VCoordinateSystem::setParticle( const OUString
& rCooSysParticle
)
119 m_aCooSysParticle
= rCooSysParticle
;
122 void VCoordinateSystem::setTransformationSceneToScreen(
123 const drawing::HomogenMatrix
& rMatrix
)
125 m_aMatrixSceneToScreen
= rMatrix
;
127 //correct transformation for axis
128 for (auto const& elem
: m_aAxisMap
)
130 VAxisBase
* pVAxis
= elem
.second
.get();
133 if(pVAxis
->getDimensionCount()==2)
134 pVAxis
->setTransformationSceneToScreen( m_aMatrixSceneToScreen
);
139 //better performance for big data
140 uno::Sequence
< sal_Int32
> VCoordinateSystem::getCoordinateSystemResolution(
141 const awt::Size
& rPageSize
, const awt::Size
& rPageResolution
)
143 uno::Sequence
<sal_Int32
> aResolution(
144 std::max
<sal_Int32
>(m_xCooSysModel
->getDimension(), 2));
145 auto aResolutionRange
= asNonConstRange(aResolution
);
146 for( auto& i
: aResolutionRange
)
149 ::basegfx::B3DTuple
aScale( BaseGFXHelper::GetScaleFromMatrix(
150 BaseGFXHelper::HomogenMatrixToB3DHomMatrix(
151 m_aMatrixSceneToScreen
) ) );
153 double fCoosysWidth
= fabs(aScale
.getX()*FIXED_SIZE_FOR_3D_CHART_VOLUME
);
154 double fCoosysHeight
= fabs(aScale
.getY()*FIXED_SIZE_FOR_3D_CHART_VOLUME
);
156 double fPageWidth
= rPageSize
.Width
;
157 double fPageHeight
= rPageSize
.Height
;
159 //factor 2 to avoid rounding problems
160 sal_Int32 nXResolution
= static_cast<sal_Int32
>(2.0*static_cast<double>(rPageResolution
.Width
)*fCoosysWidth
/fPageWidth
);
161 sal_Int32 nYResolution
= static_cast<sal_Int32
>(2.0*static_cast<double>(rPageResolution
.Height
)*fCoosysHeight
/fPageHeight
);
163 if( nXResolution
< 10 )
165 if( nYResolution
< 10 )
168 if( getPropertySwapXAndYAxis() )
169 std::swap(nXResolution
,nYResolution
);
172 if( aResolution
.getLength() == 2 )
174 aResolutionRange
[0]=nXResolution
;
175 aResolutionRange
[1]=nYResolution
;
179 //this maybe can be optimized further ...
180 sal_Int32 nMaxResolution
= std::max( nXResolution
, nYResolution
);
182 for( auto& i
: asNonConstRange(aResolution
) )
189 rtl::Reference
< Axis
> VCoordinateSystem::getAxisByDimension( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
) const
191 if( m_xCooSysModel
.is() )
192 return m_xCooSysModel
->getAxisByDimension2( nDimensionIndex
, nAxisIndex
);
196 std::vector
< rtl::Reference
< ::chart::GridProperties
> > VCoordinateSystem::getGridListFromAxis( const rtl::Reference
< Axis
>& xAxis
)
198 std::vector
< rtl::Reference
< ::chart::GridProperties
> > aRet
;
202 aRet
.push_back( xAxis
->getGridProperties2() );
203 std::vector
<rtl::Reference
<::chart::GridProperties
>> aSubGrids
= xAxis
->getSubGridProperties2();
204 aRet
.insert( aRet
.end(), aSubGrids
.begin(), aSubGrids
.end() );
210 void VCoordinateSystem::impl_adjustDimension( sal_Int32
& rDimensionIndex
)
212 rDimensionIndex
= std::clamp
<sal_Int32
>(rDimensionIndex
, 0, 2);
215 void VCoordinateSystem::impl_adjustDimensionAndIndex( sal_Int32
& rDimensionIndex
, sal_Int32
& rAxisIndex
) const
217 impl_adjustDimension( rDimensionIndex
);
219 if( rAxisIndex
< 0 || rAxisIndex
> getMaximumAxisIndexByDimension(rDimensionIndex
) )
223 void VCoordinateSystem::setExplicitCategoriesProvider( ExplicitCategoriesProvider
* pExplicitCategoriesProvider
/*takes ownership*/ )
225 m_apExplicitCategoriesProvider
.reset(pExplicitCategoriesProvider
);
228 ExplicitCategoriesProvider
* VCoordinateSystem::getExplicitCategoriesProvider()
230 return m_apExplicitCategoriesProvider
.get();
233 std::vector
< ExplicitScaleData
> VCoordinateSystem::getExplicitScales( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
) const
235 std::vector
< ExplicitScaleData
> aRet(m_aExplicitScales
);
237 impl_adjustDimensionAndIndex( nDimensionIndex
, nAxisIndex
);
238 aRet
[nDimensionIndex
]=getExplicitScale( nDimensionIndex
, nAxisIndex
);
243 std::vector
< ExplicitIncrementData
> VCoordinateSystem::getExplicitIncrements( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
) const
245 std::vector
< ExplicitIncrementData
> aRet(m_aExplicitIncrements
);
247 impl_adjustDimensionAndIndex( nDimensionIndex
, nAxisIndex
);
248 aRet
[nDimensionIndex
]=getExplicitIncrement( nDimensionIndex
, nAxisIndex
);
253 ExplicitScaleData
VCoordinateSystem::getExplicitScale( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
) const
255 ExplicitScaleData aRet
;
257 impl_adjustDimensionAndIndex( nDimensionIndex
, nAxisIndex
);
261 aRet
= m_aExplicitScales
[nDimensionIndex
];
265 tFullAxisIndex
aFullAxisIndex( nDimensionIndex
, nAxisIndex
);
266 tFullExplicitScaleMap::const_iterator aIt
= m_aSecondaryExplicitScales
.find( aFullAxisIndex
);
267 if( aIt
!= m_aSecondaryExplicitScales
.end() )
270 aRet
= m_aExplicitScales
[nDimensionIndex
];
276 ExplicitIncrementData
VCoordinateSystem::getExplicitIncrement( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
) const
278 ExplicitIncrementData aRet
;
280 impl_adjustDimensionAndIndex( nDimensionIndex
, nAxisIndex
);
284 aRet
= m_aExplicitIncrements
[nDimensionIndex
];
288 tFullAxisIndex
aFullAxisIndex( nDimensionIndex
, nAxisIndex
);
289 tFullExplicitIncrementMap::const_iterator aIt
= m_aSecondaryExplicitIncrements
.find( aFullAxisIndex
);
290 if( aIt
!= m_aSecondaryExplicitIncrements
.end() )
293 aRet
= m_aExplicitIncrements
[nDimensionIndex
];
299 OUString
VCoordinateSystem::createCIDForAxis( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
)
301 OUString
aAxisParticle( ObjectIdentifier::createParticleForAxis( nDimensionIndex
, nAxisIndex
) );
302 return ObjectIdentifier::createClassifiedIdentifierForParticles( m_aCooSysParticle
, aAxisParticle
);
304 OUString
VCoordinateSystem::createCIDForGrid( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
)
306 OUString
aGridParticle( ObjectIdentifier::createParticleForGrid( nDimensionIndex
, nAxisIndex
) );
307 return ObjectIdentifier::createClassifiedIdentifierForParticles( m_aCooSysParticle
, aGridParticle
);
310 sal_Int32
VCoordinateSystem::getMaximumAxisIndexByDimension( sal_Int32 nDimensionIndex
) const
313 for (auto const& elem
: m_aSecondaryExplicitScales
)
315 if(elem
.first
.first
==nDimensionIndex
)
317 sal_Int32 nLocalIdx
= elem
.first
.second
;
318 if( nRet
< nLocalIdx
)
325 void VCoordinateSystem::createVAxisList(
326 const rtl::Reference
<::chart::ChartModel
> & /* xChartDoc */,
327 const awt::Size
& /* rFontReferenceSize */,
328 const awt::Rectangle
& /* rMaximumSpaceForLabels */,
329 bool /* bLimitSpaceForLabels */,
330 std::vector
<std::unique_ptr
<VSeriesPlotter
>>& /*rSeriesPlotterList*/,
331 uno::Reference
<uno::XComponentContext
> const& /*rComponentContext*/)
335 void VCoordinateSystem::initVAxisInList()
338 void VCoordinateSystem::updateScalesAndIncrementsOnAxes()
342 void VCoordinateSystem::prepareAutomaticAxisScaling( ScaleAutomatism
& rScaleAutomatism
, sal_Int32 nDimIndex
, sal_Int32 nAxisIndex
)
344 bool bDateAxisX
= (rScaleAutomatism
.getScale().AxisType
== AxisType::DATE
) && (nDimIndex
== 0);
347 // This is a date X dimension. Determine proper time resolution.
348 sal_Int32 nTimeResolution
= css::chart::TimeUnit::MONTH
;
349 if( !(rScaleAutomatism
.getScale().TimeIncrement
.TimeResolution
>>= nTimeResolution
) )
351 nTimeResolution
= m_aMergedMinMaxSupplier
.calculateTimeResolutionOnXAxis();
352 rScaleAutomatism
.setAutomaticTimeResolution( nTimeResolution
);
354 m_aMergedMinMaxSupplier
.setTimeResolutionOnXAxis( nTimeResolution
, rScaleAutomatism
.getNullDate() );
357 double fMin
= std::numeric_limits
<double>::infinity();
358 double fMax
= -std::numeric_limits
<double>::infinity();
362 fMin
= m_aMergedMinMaxSupplier
.getMinimumX();
363 fMax
= m_aMergedMinMaxSupplier
.getMaximumX();
365 else if( nDimIndex
== 1 )
368 ExplicitScaleData aScale
= getExplicitScale( 0, 0 );
369 double fMaximum
= aScale
.Maximum
;
370 if (!aScale
.m_bShiftedCategoryPosition
&& aScale
.AxisType
== AxisType::DATE
)
372 // tdf#146066 Increase maximum date value by one month/year,
373 // because the automatic scaling of the Y axis was incorrect when the last Y value was the highest value.
374 Date
aMaxDate(aScale
.NullDate
);
375 aMaxDate
.AddDays(::rtl::math::approxFloor(fMaximum
));
376 switch (aScale
.TimeResolution
)
378 case css::chart::TimeUnit::MONTH
:
379 aMaxDate
= DateHelper::GetDateSomeMonthsAway(aMaxDate
, 1);
381 case css::chart::TimeUnit::YEAR
:
382 aMaxDate
= DateHelper::GetDateSomeYearsAway(aMaxDate
, 1);
385 fMaximum
= aMaxDate
- aScale
.NullDate
;
387 fMin
= m_aMergedMinMaxSupplier
.getMinimumYInRange(aScale
.Minimum
,aScale
.Maximum
, nAxisIndex
);
388 fMax
= m_aMergedMinMaxSupplier
.getMaximumYInRange(aScale
.Minimum
, fMaximum
, nAxisIndex
);
390 else if( nDimIndex
== 2 )
393 fMin
= m_aMergedMinMaxSupplier
.getMinimumZ();
394 fMax
= m_aMergedMinMaxSupplier
.getMaximumZ();
397 //merge our values with those already contained in rScaleAutomatism
398 rScaleAutomatism
.expandValueRange( fMin
, fMax
);
400 rScaleAutomatism
.setAutoScalingOptions(
401 m_aMergedMinMaxSupplier
.isExpandBorderToIncrementRhythm( nDimIndex
),
402 m_aMergedMinMaxSupplier
.isExpandIfValuesCloseToBorder( nDimIndex
),
403 m_aMergedMinMaxSupplier
.isExpandWideValuesToZero( nDimIndex
),
404 m_aMergedMinMaxSupplier
.isExpandNarrowValuesTowardZero( nDimIndex
) );
409 VAxisBase
* pVAxis
= getVAxis(nDimIndex
, nAxisIndex
);
411 rScaleAutomatism
.setMaximumAutoMainIncrementCount( pVAxis
->estimateMaximumAutoMainIncrementCount() );
414 VAxisBase
* VCoordinateSystem::getVAxis( sal_Int32 nDimensionIndex
, sal_Int32 nAxisIndex
)
416 VAxisBase
* pRet
= nullptr;
418 tFullAxisIndex
aFullAxisIndex( nDimensionIndex
, nAxisIndex
);
420 tVAxisMap::const_iterator aIt
= m_aAxisMap
.find( aFullAxisIndex
);
421 if (aIt
!= m_aAxisMap
.cend())
422 pRet
= aIt
->second
.get();
427 void VCoordinateSystem::setExplicitScaleAndIncrement(
428 sal_Int32 nDimensionIndex
429 , sal_Int32 nAxisIndex
430 , const ExplicitScaleData
& rExplicitScale
431 , const ExplicitIncrementData
& rExplicitIncrement
)
433 impl_adjustDimension( nDimensionIndex
);
437 m_aExplicitScales
[nDimensionIndex
]=rExplicitScale
;
438 m_aExplicitIncrements
[nDimensionIndex
]=rExplicitIncrement
;
442 tFullAxisIndex
aFullAxisIndex( nDimensionIndex
, nAxisIndex
);
443 m_aSecondaryExplicitScales
[aFullAxisIndex
] = rExplicitScale
;
444 m_aSecondaryExplicitIncrements
[aFullAxisIndex
] = rExplicitIncrement
;
448 void VCoordinateSystem::set3DWallPositions( CuboidPlanePosition eLeftWallPos
, CuboidPlanePosition eBackWallPos
, CuboidPlanePosition eBottomPos
)
450 m_eLeftWallPos
= eLeftWallPos
;
451 m_eBackWallPos
= eBackWallPos
;
452 m_eBottomPos
= eBottomPos
;
455 void VCoordinateSystem::createMaximumAxesLabels()
457 for (auto const&[unused
, pVAxis
] : m_aAxisMap
)
462 if (pVAxis
->getDimensionCount() == 2)
463 pVAxis
->setTransformationSceneToScreen(m_aMatrixSceneToScreen
);
464 pVAxis
->createMaximumLabels();
468 void VCoordinateSystem::createAxesLabels()
470 for (auto const&[unused
, pVAxis
] : m_aAxisMap
)
475 if (pVAxis
->getDimensionCount() == 2)
476 pVAxis
->setTransformationSceneToScreen(m_aMatrixSceneToScreen
);
477 pVAxis
->createLabels();
482 void VCoordinateSystem::updatePositions()
484 for (auto const&[unused
, pVAxis
] : m_aAxisMap
)
489 if (pVAxis
->getDimensionCount() == 2)
490 pVAxis
->setTransformationSceneToScreen( m_aMatrixSceneToScreen
);
491 pVAxis
->updatePositions();
496 void VCoordinateSystem::createAxesShapes()
498 for (auto const&[aFullAxisIndex
, pVAxis
] : m_aAxisMap
)
502 auto const&[nDimensionIndex
, nAxisIndex
] = aFullAxisIndex
;
504 if (pVAxis
->getDimensionCount() == 2)
505 pVAxis
->setTransformationSceneToScreen( m_aMatrixSceneToScreen
);
509 if (nDimensionIndex
== 0)
511 if( m_aExplicitScales
[1].AxisType
!=AxisType::CATEGORY
)
512 pVAxis
->setExtraLinePositionAtOtherAxis(
513 m_aExplicitScales
[1].Origin
);
515 else if (nDimensionIndex
== 1)
517 if( m_aExplicitScales
[0].AxisType
!=AxisType::CATEGORY
)
518 pVAxis
->setExtraLinePositionAtOtherAxis(
519 m_aExplicitScales
[0].Origin
);
523 pVAxis
->createShapes();
527 void VCoordinateSystem::createGridShapes()
530 void VCoordinateSystem::addMinimumAndMaximumSupplier( MinimumAndMaximumSupplier
* pMinimumAndMaximumSupplier
)
532 m_aMergedMinMaxSupplier
.addMinimumAndMaximumSupplier(pMinimumAndMaximumSupplier
);
535 bool VCoordinateSystem::hasMinimumAndMaximumSupplier( MinimumAndMaximumSupplier
* pMinimumAndMaximumSupplier
)
537 return m_aMergedMinMaxSupplier
.hasMinimumAndMaximumSupplier(pMinimumAndMaximumSupplier
);
540 void VCoordinateSystem::clearMinimumAndMaximumSupplierList()
542 m_aMergedMinMaxSupplier
.clearMinimumAndMaximumSupplierList();
545 bool VCoordinateSystem::getPropertySwapXAndYAxis() const
547 bool bSwapXAndY
= false;
548 if( m_xCooSysModel
.is()) try
550 m_xCooSysModel
->getPropertyValue( u
"SwapXAndYAxis"_ustr
) >>= bSwapXAndY
;
552 catch( const uno::Exception
& )
554 TOOLS_WARN_EXCEPTION("chart2", "" );
559 bool VCoordinateSystem::needSeriesNamesForAxis() const
561 return ( m_xCooSysModel
.is() && m_xCooSysModel
->getDimension() == 3 );
563 void VCoordinateSystem::setSeriesNamesForAxis( const Sequence
< OUString
>& rSeriesNames
)
565 m_aSeriesNamesForZAxis
= rSeriesNames
;
568 sal_Int32
VCoordinateSystem::getNumberFormatKeyForAxis(
569 const rtl::Reference
< Axis
>& xAxis
570 , const rtl::Reference
<::chart::ChartModel
>& xChartDoc
)
572 return ExplicitValueProvider::getExplicitNumberFormatKeyForAxis(
573 xAxis
, m_xCooSysModel
, xChartDoc
);
578 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */