Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / chart2 / source / view / main / PlottingPositionHelper.cxx
blobd26e9331ba444b2417d3971c04dc833756f81cf9
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <PlottingPositionHelper.hxx>
21 #include <CommonConverters.hxx>
22 #include <Linear3DTransformation.hxx>
23 #include <VPolarTransformation.hxx>
24 #include <ShapeFactory.hxx>
25 #include <PropertyMapper.hxx>
26 #include <defines.hxx>
28 #include <com/sun/star/chart/TimeUnit.hpp>
29 #include <com/sun/star/chart2/AxisType.hpp>
30 #include <com/sun/star/drawing/DoubleSequence.hpp>
31 #include <com/sun/star/drawing/Position3D.hpp>
32 #include <com/sun/star/drawing/XShapes.hpp>
34 #include <rtl/math.hxx>
36 namespace chart
38 using namespace ::com::sun::star;
39 using namespace ::com::sun::star::chart2;
41 PlottingPositionHelper::PlottingPositionHelper()
42 : m_aScales()
43 , m_aMatrixScreenToScene()
44 , m_bSwapXAndY( false )
45 , m_nXResolution( 1000 )
46 , m_nYResolution( 1000 )
47 , m_nZResolution( 1000 )
48 , m_bMaySkipPointsInRegressionCalculation( true )
49 , m_bDateAxis(false)
50 , m_nTimeResolution( css::chart::TimeUnit::DAY )
51 , m_aNullDate(30,12,1899)
52 , m_fScaledCategoryWidth(1.0)
53 , m_bAllowShiftXAxisPos(false)
54 , m_bAllowShiftZAxisPos(false)
57 PlottingPositionHelper::PlottingPositionHelper( const PlottingPositionHelper& rSource )
58 : m_aScales( rSource.m_aScales )
59 , m_aMatrixScreenToScene( rSource.m_aMatrixScreenToScene )
60 // m_xTransformationLogicToScene( nullptr ) //should be recalculated
61 , m_bSwapXAndY( rSource.m_bSwapXAndY )
62 , m_nXResolution( rSource.m_nXResolution )
63 , m_nYResolution( rSource.m_nYResolution )
64 , m_nZResolution( rSource.m_nZResolution )
65 , m_bMaySkipPointsInRegressionCalculation( rSource.m_bMaySkipPointsInRegressionCalculation )
66 , m_bDateAxis( rSource.m_bDateAxis )
67 , m_nTimeResolution( rSource.m_nTimeResolution )
68 , m_aNullDate( rSource.m_aNullDate )
69 , m_fScaledCategoryWidth( rSource.m_fScaledCategoryWidth )
70 , m_bAllowShiftXAxisPos( rSource.m_bAllowShiftXAxisPos )
71 , m_bAllowShiftZAxisPos( rSource.m_bAllowShiftZAxisPos )
75 PlottingPositionHelper::~PlottingPositionHelper()
80 std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::clone() const
82 return std::make_unique<PlottingPositionHelper>(*this);
85 std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::createSecondaryPosHelper( const ExplicitScaleData& rSecondaryScale )
87 auto pRet = clone();
88 pRet->m_aScales[1]=rSecondaryScale;
89 return pRet;
92 void PlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix)
94 m_aMatrixScreenToScene = HomogenMatrixToB3DHomMatrix(rMatrix);
95 m_xTransformationLogicToScene = nullptr;
98 void PlottingPositionHelper::setScales( const std::vector< ExplicitScaleData >& rScales, bool bSwapXAndYAxis )
100 m_aScales = rScales;
101 m_bSwapXAndY = bSwapXAndYAxis;
102 m_xTransformationLogicToScene = nullptr;
105 uno::Reference< XTransformation > PlottingPositionHelper::getTransformationScaledLogicToScene() const
107 //this is a standard transformation for a cartesian coordinate system
109 //transformation from 2) to 4) //@todo 2) and 4) need an ink to a document
111 //we need to apply this transformation to each geometric object because of a bug/problem
112 //of the old drawing layer (the UNO_NAME_3D_EXTRUDE_DEPTH is an integer value instead of a double )
113 if(!m_xTransformationLogicToScene.is())
115 ::basegfx::B3DHomMatrix aMatrix;
116 double MinX = getLogicMinX();
117 double MinY = getLogicMinY();
118 double MinZ = getLogicMinZ();
119 double MaxX = getLogicMaxX();
120 double MaxY = getLogicMaxY();
121 double MaxZ = getLogicMaxZ();
123 AxisOrientation nXAxisOrientation = m_aScales[0].Orientation;
124 AxisOrientation nYAxisOrientation = m_aScales[1].Orientation;
125 AxisOrientation nZAxisOrientation = m_aScales[2].Orientation;
127 //apply scaling
128 doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
129 doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
131 if(m_bSwapXAndY)
133 std::swap(MinX,MinY);
134 std::swap(MaxX,MaxY);
135 std::swap(nXAxisOrientation,nYAxisOrientation);
138 double fWidthX = MaxX - MinX;
139 double fWidthY = MaxY - MinY;
140 double fWidthZ = MaxZ - MinZ;
142 double fScaleDirectionX = nXAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
143 double fScaleDirectionY = nYAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
144 double fScaleDirectionZ = nZAxisOrientation==AxisOrientation_MATHEMATICAL ? -1.0 : 1.0;
146 double fScaleX = fScaleDirectionX*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthX;
147 double fScaleY = fScaleDirectionY*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthY;
148 double fScaleZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ;
150 aMatrix.scale(fScaleX, fScaleY, fScaleZ);
152 if( nXAxisOrientation==AxisOrientation_MATHEMATICAL )
153 aMatrix.translate(-MinX*fScaleX, 0.0, 0.0);
154 else
155 aMatrix.translate(-MaxX*fScaleX, 0.0, 0.0);
156 if( nYAxisOrientation==AxisOrientation_MATHEMATICAL )
157 aMatrix.translate(0.0, -MinY*fScaleY, 0.0);
158 else
159 aMatrix.translate(0.0, -MaxY*fScaleY, 0.0);
160 if( nZAxisOrientation==AxisOrientation_MATHEMATICAL )
161 aMatrix.translate(0.0, 0.0, -MaxZ*fScaleZ);//z direction in draw is reverse mathematical direction
162 else
163 aMatrix.translate(0.0, 0.0, -MinZ*fScaleZ);
165 aMatrix = m_aMatrixScreenToScene*aMatrix;
167 m_xTransformationLogicToScene = new Linear3DTransformation(B3DHomMatrixToHomogenMatrix( aMatrix ),m_bSwapXAndY);
169 return m_xTransformationLogicToScene;
172 drawing::Position3D PlottingPositionHelper::transformLogicToScene(
173 double fX, double fY, double fZ, bool bClip ) const
175 doLogicScaling( &fX,&fY,&fZ );
176 if(bClip)
177 clipScaledLogicValues( &fX,&fY,&fZ );
179 return transformScaledLogicToScene( fX, fY, fZ, false );
182 drawing::Position3D PlottingPositionHelper::transformScaledLogicToScene(
183 double fX, double fY, double fZ, bool bClip ) const
185 if( bClip )
186 clipScaledLogicValues( &fX,&fY,&fZ );
188 drawing::Position3D aPos( fX, fY, fZ);
190 uno::Reference< XTransformation > xTransformation =
191 getTransformationScaledLogicToScene();
192 uno::Sequence< double > aSeq =
193 xTransformation->transform( Position3DToSequence(aPos) );
194 return SequenceToPosition3D(aSeq);
197 awt::Point PlottingPositionHelper::transformSceneToScreenPosition( const drawing::Position3D& rScenePosition3D
198 , const uno::Reference< drawing::XShapes >& xSceneTarget
199 , ShapeFactory* pShapeFactory
200 , sal_Int32 nDimensionCount )
202 //@todo would like to have a cheaper method to do this transformation
203 awt::Point aScreenPoint( static_cast<sal_Int32>(rScenePosition3D.PositionX), static_cast<sal_Int32>(rScenePosition3D.PositionY) );
205 //transformation from scene to screen (only necessary for 3D):
206 if(nDimensionCount==3)
208 //create 3D anchor shape
209 tPropertyNameMap aDummyPropertyNameMap;
210 uno::Reference< drawing::XShape > xShape3DAnchor = pShapeFactory->createCube( xSceneTarget
211 , rScenePosition3D,drawing::Direction3D(1,1,1)
212 , 0, nullptr, aDummyPropertyNameMap);
213 //get 2D position from xShape3DAnchor
214 aScreenPoint = xShape3DAnchor->getPosition();
215 xSceneTarget->remove(xShape3DAnchor);
217 return aScreenPoint;
220 void PlottingPositionHelper::transformScaledLogicToScene( drawing::PolyPolygonShape3D& rPolygon ) const
222 drawing::Position3D aScenePosition;
223 for( sal_Int32 nS = rPolygon.SequenceX.getLength(); nS--;)
225 drawing::DoubleSequence& xValues = rPolygon.SequenceX[nS];
226 drawing::DoubleSequence& yValues = rPolygon.SequenceY[nS];
227 drawing::DoubleSequence& zValues = rPolygon.SequenceZ[nS];
228 for( sal_Int32 nP = xValues.getLength(); nP--; )
230 double& fX = xValues[nP];
231 double& fY = yValues[nP];
232 double& fZ = zValues[nP];
233 aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true );
234 fX = aScenePosition.PositionX;
235 fY = aScenePosition.PositionY;
236 fZ = aScenePosition.PositionZ;
241 void PlottingPositionHelper::clipScaledLogicValues( double* pX, double* pY, double* pZ ) const
243 //get logic clip values:
244 double MinX = getLogicMinX();
245 double MinY = getLogicMinY();
246 double MinZ = getLogicMinZ();
247 double MaxX = getLogicMaxX();
248 double MaxY = getLogicMaxY();
249 double MaxZ = getLogicMaxZ();
251 //apply scaling
252 doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
253 doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
255 if(pX)
257 if( *pX < MinX )
258 *pX = MinX;
259 else if( *pX > MaxX )
260 *pX = MaxX;
262 if(pY)
264 if( *pY < MinY )
265 *pY = MinY;
266 else if( *pY > MaxY )
267 *pY = MaxY;
269 if(pZ)
271 if( *pZ < MinZ )
272 *pZ = MinZ;
273 else if( *pZ > MaxZ )
274 *pZ = MaxZ;
278 basegfx::B2DRectangle PlottingPositionHelper::getScaledLogicClipDoubleRect() const
280 //get logic clip values:
281 double MinX = getLogicMinX();
282 double MinY = getLogicMinY();
283 double MinZ = getLogicMinZ();
284 double MaxX = getLogicMaxX();
285 double MaxY = getLogicMaxY();
286 double MaxZ = getLogicMaxZ();
288 //apply scaling
289 doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
290 doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
292 basegfx::B2DRectangle aRet( MinX, MaxY, MaxX, MinY );
293 return aRet;
296 drawing::Direction3D PlottingPositionHelper::getScaledLogicWidth() const
298 drawing::Direction3D aRet;
300 double MinX = getLogicMinX();
301 double MinY = getLogicMinY();
302 double MinZ = getLogicMinZ();
303 double MaxX = getLogicMaxX();
304 double MaxY = getLogicMaxY();
305 double MaxZ = getLogicMaxZ();
307 doLogicScaling( &MinX, &MinY, &MinZ );
308 doLogicScaling( &MaxX, &MaxY, &MaxZ);
310 aRet.DirectionX = MaxX - MinX;
311 aRet.DirectionY = MaxY - MinY;
312 aRet.DirectionZ = MaxZ - MinZ;
313 return aRet;
316 PolarPlottingPositionHelper::PolarPlottingPositionHelper()
317 : m_fRadiusOffset(0.0)
318 , m_fAngleDegreeOffset(90.0)
319 , m_aUnitCartesianToScene()
321 m_bMaySkipPointsInRegressionCalculation = false;
324 PolarPlottingPositionHelper::PolarPlottingPositionHelper( const PolarPlottingPositionHelper& rSource )
325 : PlottingPositionHelper(rSource)
326 , m_fRadiusOffset( rSource.m_fRadiusOffset )
327 , m_fAngleDegreeOffset( rSource.m_fAngleDegreeOffset )
328 , m_aUnitCartesianToScene( rSource.m_aUnitCartesianToScene )
332 PolarPlottingPositionHelper::~PolarPlottingPositionHelper()
336 std::unique_ptr<PlottingPositionHelper> PolarPlottingPositionHelper::clone() const
338 return std::make_unique<PolarPlottingPositionHelper>(*this);
341 void PolarPlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix)
343 PlottingPositionHelper::setTransformationSceneToScreen( rMatrix);
344 m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene );
346 void PolarPlottingPositionHelper::setScales( const std::vector< ExplicitScaleData >& rScales, bool bSwapXAndYAxis )
348 PlottingPositionHelper::setScales( rScales, bSwapXAndYAxis );
349 m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene );
352 ::basegfx::B3DHomMatrix PolarPlottingPositionHelper::impl_calculateMatrixUnitCartesianToScene( const ::basegfx::B3DHomMatrix& rMatrixScreenToScene ) const
354 ::basegfx::B3DHomMatrix aRet;
356 if( m_aScales.empty() )
357 return aRet;
359 double fTranslate =1.0;
360 double fScale =FIXED_SIZE_FOR_3D_CHART_VOLUME/2.0;
362 double fTranslateLogicZ;
363 double fScaleLogicZ;
365 double fScaleDirectionZ = m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
366 double MinZ = getLogicMinZ();
367 double MaxZ = getLogicMaxZ();
368 doLogicScaling( nullptr, nullptr, &MinZ );
369 doLogicScaling( nullptr, nullptr, &MaxZ );
370 double fWidthZ = MaxZ - MinZ;
372 if( m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL )
373 fTranslateLogicZ=MinZ;
374 else
375 fTranslateLogicZ=MaxZ;
376 fScaleLogicZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ;
379 double fTranslateX = fTranslate;
380 double fTranslateY = fTranslate;
381 double fTranslateZ = fTranslateLogicZ;
383 double fScaleX = fScale;
384 double fScaleY = fScale;
385 double fScaleZ = fScaleLogicZ;
387 aRet.translate(fTranslateX, fTranslateY, fTranslateZ);//x first
388 aRet.scale(fScaleX, fScaleY, fScaleZ);//x first
390 aRet = rMatrixScreenToScene * aRet;
391 return aRet;
394 uno::Reference< XTransformation > PolarPlottingPositionHelper::getTransformationScaledLogicToScene() const
396 if( !m_xTransformationLogicToScene.is() )
397 m_xTransformationLogicToScene = new VPolarTransformation(*this);
398 return m_xTransformationLogicToScene;
401 double PolarPlottingPositionHelper::getWidthAngleDegree( double& fStartLogicValueOnAngleAxis, double& fEndLogicValueOnAngleAxis ) const
403 const ExplicitScaleData& rAngleScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0];
404 if( rAngleScale.Orientation != AxisOrientation_MATHEMATICAL )
406 double fHelp = fEndLogicValueOnAngleAxis;
407 fEndLogicValueOnAngleAxis = fStartLogicValueOnAngleAxis;
408 fStartLogicValueOnAngleAxis = fHelp;
411 double fStartAngleDegree = transformToAngleDegree( fStartLogicValueOnAngleAxis );
412 double fEndAngleDegree = transformToAngleDegree( fEndLogicValueOnAngleAxis );
413 double fWidthAngleDegree = fEndAngleDegree - fStartAngleDegree;
415 if( ::rtl::math::approxEqual( fStartAngleDegree, fEndAngleDegree )
416 && !::rtl::math::approxEqual( fStartLogicValueOnAngleAxis, fEndLogicValueOnAngleAxis ) )
417 fWidthAngleDegree = 360.0;
419 // tdf#123504: both 0 and 360 are valid and different values here!
420 while (fWidthAngleDegree < 0.0)
421 fWidthAngleDegree += 360.0;
422 while (fWidthAngleDegree > 360.0)
423 fWidthAngleDegree -= 360.0;
425 return fWidthAngleDegree;
428 //This method does a lot of computation for understanding which scale to
429 //utilize and if reverse orientation should be used. Indeed, for a pie or donut,
430 //the final result is as simple as multiplying by 360 and adding
431 //`m_fAngleDegreeOffset`.
432 double PolarPlottingPositionHelper::transformToAngleDegree( double fLogicValueOnAngleAxis, bool bDoScaling ) const
434 double fRet=0.0;
436 double fAxisAngleScaleDirection = 1.0;
438 const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0];
439 if(rScale.Orientation != AxisOrientation_MATHEMATICAL)
440 fAxisAngleScaleDirection *= -1.0;
443 double MinAngleValue = 0.0;
444 double MaxAngleValue = 0.0;
446 double MinX = getLogicMinX();
447 double MinY = getLogicMinY();
448 double MaxX = getLogicMaxX();
449 double MaxY = getLogicMaxY();
450 double MinZ = getLogicMinZ();
451 double MaxZ = getLogicMaxZ();
453 doLogicScaling( &MinX, &MinY, &MinZ );
454 doLogicScaling( &MaxX, &MaxY, &MaxZ);
456 MinAngleValue = m_bSwapXAndY ? MinY : MinX;
457 MaxAngleValue = m_bSwapXAndY ? MaxY : MaxX;
460 double fScaledLogicAngleValue = 0.0;
461 if(bDoScaling)
463 double fX = m_bSwapXAndY ? getLogicMaxX() : fLogicValueOnAngleAxis;
464 double fY = m_bSwapXAndY ? fLogicValueOnAngleAxis : getLogicMaxY();
465 double fZ = getLogicMaxZ();
466 clipLogicValues( &fX, &fY, &fZ );
467 doLogicScaling( &fX, &fY, &fZ );
468 fScaledLogicAngleValue = m_bSwapXAndY ? fY : fX;
470 else
471 fScaledLogicAngleValue = fLogicValueOnAngleAxis;
473 fRet = m_fAngleDegreeOffset
474 + fAxisAngleScaleDirection*(fScaledLogicAngleValue-MinAngleValue)*360.0
475 /fabs(MaxAngleValue-MinAngleValue);
476 // tdf#123504: both 0 and 360 are valid and different values here!
477 while (fRet > 360.0)
478 fRet -= 360.0;
479 while (fRet < 0)
480 fRet += 360.0;
481 return fRet;
485 * Given a value in the radius axis scale range, it returns, in the simplest
486 * case (that is when `m_fRadiusOffset` is zero), the normalized value; when
487 * `m_fRadiusOffset` is not zero (e.g. as in the case of a donut), the interval
488 * used for normalization is extended by `m_fRadiusOffset`: if the axis
489 * orientation is not reversed the new interval becomes
490 * [scale.Minimum - m_fRadiusOffset, scale.Maximum] else it becomes
491 * [scale.Minimum, scale.Maximum + m_fRadiusOffset].
492 * Pay attention here! For the latter case, since the axis orientation is
493 * reversed, the normalization is reversed too. Indeed, we have
494 * `transformToRadius(scale.Maximum + m_fRadiusOffset) = 0` and
495 * `transformToRadius(scale.Minimum) = 1`.
497 * For a pie chart the radius axis scale range is initialized by the
498 * `getMinimum` and `getMaximum` methods of the `PieChart` object (see notes
499 * for `VCoordinateSystem::prepareAutomaticAxisScaling`).
500 * So we have scale.Minimum = 0.5 (always constant!) and
501 * scale.Maximum = 0.5 + number_of_rings + max_offset
502 * (see notes for `PieChart::getMaxOffset`).
503 * Hence we get the following general formulas for computing normalized inner
504 * and outer radius:
506 * 1- transformToRadius(inner_radius) =
507 * (number_of_rings - (ring_index + 1) + m_fRadiusOffset)
508 * / (number_of_rings + max_offset + m_fRadiusOffset)
510 * 2- transformToRadius(outer_radius) =
511 * (1 + number_of_rings - (ring_index + 1) + m_fRadiusOffset)
512 * / (number_of_rings + max_offset + m_fRadiusOffset).
514 * Here you have to take into account that values for inner and outer radius
515 * are swapped since the radius axis is reversed (See notes for
516 * `PiePositionHelper::getInnerAndOuterRadius`). So indeed inner_radius is
517 * the outer and outer_radius is the inner. Anyway still because of the reverse
518 * orientation, the normalization performed by `transformToRadius` is reversed
519 * too, as we have seen above. Hence `transformToRadius(inner_radius)` is
520 * really the normalized inner radius and `transformToRadius(outer_radius)` is
521 * really the normalized outer radius.
523 * Some basic examples where we apply the above formulas:
524 * 1- For a non-exploded pie chart we have:
525 * `transformToRadius(inner_radius) = 0`,
526 * `transformToRadius(outer_radius) = 1`.
527 * 2- For a non-exploded donut with a single ring we have:
528 * `transformToRadius(inner_radius) =
529 * m_fRadiusOffset/(1 + m_fRadiusOffset)`,
530 * `transformToRadius(outer_radius) =
531 * (1 + m_fRadiusOffset)/(1 + m_fRadiusOffset) = 1`.
532 * 3- For an exploded pie chart we have:
533 * `transformToRadius(inner_radius) = 0/(1 + max_offset) = 0`,
534 * `transformToRadius(outer_radius) = 1/(1 + max_offset)`.
536 * The third example needs some remark. Both the logical inner and outer
537 * radius passed to `transformToRadius` are offset by `max_offset`.
538 * However the returned normalized values do not contain any (normalized)
539 * offset term at all, otherwise the returned values would be
540 * `max_offset/(1 + max_offset)` and `1`. Hence, for exploded pie/donut,
541 * `transformToRadius` returns the normalized value of radii without any
542 * offset term. These values are smaller than in the non-exploded case by an
543 * amount equals to the value of the normalized maximum offset
544 * (`max_offset/(1 + max_offset)` in the example above). That is due to the
545 * fact that the normalization keeps into account the space needed for the
546 * offset. This is the correct behavior, in fact the offset for the current
547 * slice could be different from the maximum offset.
548 * These remarks should clarify why the `PieChart::createDataPoint` and
549 * `PieChart::createTextLabelShape` methods add the normalized offset (for the
550 * current slice) to the normalized radii in order to achieve the correct
551 * placement of slice and text shapes.
553 double PolarPlottingPositionHelper::transformToRadius( double fLogicValueOnRadiusAxis, bool bDoScaling ) const
555 double fNormalRadius = 0.0;
557 double fScaledLogicRadiusValue = 0.0;
558 double fX = m_bSwapXAndY ? fLogicValueOnRadiusAxis: getLogicMaxX();
559 double fY = m_bSwapXAndY ? getLogicMaxY() : fLogicValueOnRadiusAxis;
560 if(bDoScaling)
561 doLogicScaling( &fX, &fY, nullptr );
563 fScaledLogicRadiusValue = m_bSwapXAndY ? fX : fY;
565 bool bMinIsInnerRadius = true;
566 const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1];
567 if(rScale.Orientation != AxisOrientation_MATHEMATICAL)
568 bMinIsInnerRadius = false;
570 double fInnerScaledLogicRadius=0.0;
571 double fOuterScaledLogicRadius=0.0;
573 double MinX = getLogicMinX();
574 double MinY = getLogicMinY();
575 doLogicScaling( &MinX, &MinY, nullptr );
576 double MaxX = getLogicMaxX();
577 double MaxY = getLogicMaxY();
578 doLogicScaling( &MaxX, &MaxY, nullptr );
580 double fMin = m_bSwapXAndY ? MinX : MinY;
581 double fMax = m_bSwapXAndY ? MaxX : MaxY;
583 fInnerScaledLogicRadius = bMinIsInnerRadius ? fMin : fMax;
584 fOuterScaledLogicRadius = bMinIsInnerRadius ? fMax : fMin;
587 if( bMinIsInnerRadius )
588 fInnerScaledLogicRadius -= fabs(m_fRadiusOffset);
589 else
590 fInnerScaledLogicRadius += fabs(m_fRadiusOffset);
591 fNormalRadius = (fScaledLogicRadiusValue-fInnerScaledLogicRadius)/(fOuterScaledLogicRadius-fInnerScaledLogicRadius);
593 return fNormalRadius;
596 drawing::Position3D PolarPlottingPositionHelper::transformLogicToScene( double fX, double fY, double fZ, bool bClip ) const
598 if(bClip)
599 clipLogicValues( &fX,&fY,&fZ );
600 double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX;
601 double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY;
602 return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ );
605 drawing::Position3D PolarPlottingPositionHelper::transformScaledLogicToScene( double fX, double fY, double fZ, bool bClip ) const
607 if(bClip)
608 clipScaledLogicValues( &fX,&fY,&fZ );
609 double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX;
610 double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY;
611 return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ, false );
613 drawing::Position3D PolarPlottingPositionHelper::transformUnitCircleToScene( double fUnitAngleDegree, double fUnitRadius
614 , double fLogicZ ) const
616 double fAnglePi = basegfx::deg2rad(fUnitAngleDegree);
618 double fX=fUnitRadius*rtl::math::cos(fAnglePi);
619 double fY=fUnitRadius*rtl::math::sin(fAnglePi);
620 double fZ=fLogicZ;
622 //!! applying matrix to vector does ignore translation, so it is important to use a B3DPoint here instead of B3DVector
623 ::basegfx::B3DPoint aPoint(fX,fY,fZ);
624 ::basegfx::B3DPoint aRet = m_aUnitCartesianToScene * aPoint;
625 return B3DPointToPosition3D(aRet);
628 drawing::Position3D PolarPlottingPositionHelper::transformAngleRadiusToScene( double fLogicValueOnAngleAxis, double fLogicValueOnRadiusAxis, double fLogicZ, bool bDoScaling ) const
630 double fUnitAngleDegree = transformToAngleDegree(fLogicValueOnAngleAxis,bDoScaling);
631 double fUnitRadius = transformToRadius(fLogicValueOnRadiusAxis,bDoScaling);
633 return transformUnitCircleToScene( fUnitAngleDegree, fUnitRadius, fLogicZ );
636 double PolarPlottingPositionHelper::getOuterLogicRadius() const
638 const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1];
639 if( rScale.Orientation==AxisOrientation_MATHEMATICAL )
640 return rScale.Maximum;
641 else
642 return rScale.Minimum;
645 bool PlottingPositionHelper::isPercentY() const
647 return m_aScales[1].AxisType==AxisType::PERCENT;
650 double PlottingPositionHelper::getBaseValueY() const
652 return m_aScales[1].Origin;
655 void PlottingPositionHelper::setTimeResolution( long nTimeResolution, const Date& rNullDate )
657 m_nTimeResolution = nTimeResolution;
658 m_aNullDate = rNullDate;
660 //adapt category width
661 double fCategoryWidth = 1.0;
662 if( !m_aScales.empty() )
664 if( m_aScales[0].AxisType == css::chart2::AxisType::DATE )
666 m_bDateAxis = true;
667 if( nTimeResolution == css::chart::TimeUnit::YEAR )
669 const double fMonthCount = 12.0;//todo: this depends on the DateScaling and must be adjusted in case we use more generic calendars in future
670 fCategoryWidth = fMonthCount;
674 setScaledCategoryWidth(fCategoryWidth);
677 void PlottingPositionHelper::setScaledCategoryWidth( double fScaledCategoryWidth )
679 m_fScaledCategoryWidth = fScaledCategoryWidth;
681 void PlottingPositionHelper::AllowShiftXAxisPos( bool bAllowShift )
683 m_bAllowShiftXAxisPos = bAllowShift;
685 void PlottingPositionHelper::AllowShiftZAxisPos( bool bAllowShift )
687 m_bAllowShiftZAxisPos = bAllowShift;
692 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */