tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / chart2 / source / view / main / PlottingPositionHelper.cxx
blob0f8c223537b2e4992ce31867b52920c5f1dd6dd2
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/Position3D.hpp>
32 #include <rtl/math.hxx>
34 namespace chart
36 using namespace ::com::sun::star;
37 using namespace ::com::sun::star::chart2;
39 XTransformation2::~XTransformation2() {}
41 PlottingPositionHelper::PlottingPositionHelper()
42 : m_bSwapXAndY( false )
43 , m_nXResolution( 1000 )
44 , m_nYResolution( 1000 )
45 , m_nZResolution( 1000 )
46 , m_bMaySkipPointsInRegressionCalculation( true )
47 , m_bDateAxis(false)
48 , m_nTimeResolution( css::chart::TimeUnit::DAY )
49 , m_aNullDate(30,12,1899)
50 , m_fScaledCategoryWidth(1.0)
51 , m_bAllowShiftXAxisPos(false)
52 , m_bAllowShiftZAxisPos(false)
55 PlottingPositionHelper::PlottingPositionHelper( const PlottingPositionHelper& rSource )
56 : m_aScales( rSource.m_aScales )
57 , m_aMatrixScreenToScene( rSource.m_aMatrixScreenToScene )
58 // m_xTransformationLogicToScene( nullptr ) //should be recalculated
59 , m_bSwapXAndY( rSource.m_bSwapXAndY )
60 , m_nXResolution( rSource.m_nXResolution )
61 , m_nYResolution( rSource.m_nYResolution )
62 , m_nZResolution( rSource.m_nZResolution )
63 , m_bMaySkipPointsInRegressionCalculation( rSource.m_bMaySkipPointsInRegressionCalculation )
64 , m_bDateAxis( rSource.m_bDateAxis )
65 , m_nTimeResolution( rSource.m_nTimeResolution )
66 , m_aNullDate( rSource.m_aNullDate )
67 , m_fScaledCategoryWidth( rSource.m_fScaledCategoryWidth )
68 , m_bAllowShiftXAxisPos( rSource.m_bAllowShiftXAxisPos )
69 , m_bAllowShiftZAxisPos( rSource.m_bAllowShiftZAxisPos )
73 PlottingPositionHelper::~PlottingPositionHelper()
78 std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::clone() const
80 return std::make_unique<PlottingPositionHelper>(*this);
83 std::unique_ptr<PlottingPositionHelper> PlottingPositionHelper::createSecondaryPosHelper( const ExplicitScaleData& rSecondaryScale )
85 auto pRet = clone();
86 pRet->m_aScales[1]=rSecondaryScale;
87 return pRet;
90 void PlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix)
92 m_aMatrixScreenToScene = HomogenMatrixToB3DHomMatrix(rMatrix);
93 m_xTransformationLogicToScene = nullptr;
96 void PlottingPositionHelper::setScales( std::vector< ExplicitScaleData >&& rScales, bool bSwapXAndYAxis )
98 m_aScales = std::move(rScales);
99 m_bSwapXAndY = bSwapXAndYAxis;
100 m_xTransformationLogicToScene = nullptr;
103 ::chart::XTransformation2* PlottingPositionHelper::getTransformationScaledLogicToScene() const
105 //this is a standard transformation for a cartesian coordinate system
107 //transformation from 2) to 4) //@todo 2) and 4) need an ink to a document
109 //we need to apply this transformation to each geometric object because of a bug/problem
110 //of the old drawing layer (the UNO_NAME_3D_EXTRUDE_DEPTH is an integer value instead of a double )
111 if(!m_xTransformationLogicToScene)
113 ::basegfx::B3DHomMatrix aMatrix;
114 double MinX = getLogicMinX();
115 double MinY = getLogicMinY();
116 double MinZ = getLogicMinZ();
117 double MaxX = getLogicMaxX();
118 double MaxY = getLogicMaxY();
119 double MaxZ = getLogicMaxZ();
121 AxisOrientation nXAxisOrientation = m_aScales[0].Orientation;
122 AxisOrientation nYAxisOrientation = m_aScales[1].Orientation;
123 AxisOrientation nZAxisOrientation = m_aScales[2].Orientation;
125 //apply scaling
126 doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
127 doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
129 if(m_bSwapXAndY)
131 std::swap(MinX,MinY);
132 std::swap(MaxX,MaxY);
133 std::swap(nXAxisOrientation,nYAxisOrientation);
136 double fWidthX = MaxX - MinX;
137 double fWidthY = MaxY - MinY;
138 double fWidthZ = MaxZ - MinZ;
140 double fScaleDirectionX = nXAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
141 double fScaleDirectionY = nYAxisOrientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
142 double fScaleDirectionZ = nZAxisOrientation==AxisOrientation_MATHEMATICAL ? -1.0 : 1.0;
144 double fScaleX = fScaleDirectionX*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthX;
145 double fScaleY = fScaleDirectionY*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthY;
146 double fScaleZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ;
148 aMatrix.scale(fScaleX, fScaleY, fScaleZ);
150 if( nXAxisOrientation==AxisOrientation_MATHEMATICAL )
151 aMatrix.translate(-MinX*fScaleX, 0.0, 0.0);
152 else
153 aMatrix.translate(-MaxX*fScaleX, 0.0, 0.0);
154 if( nYAxisOrientation==AxisOrientation_MATHEMATICAL )
155 aMatrix.translate(0.0, -MinY*fScaleY, 0.0);
156 else
157 aMatrix.translate(0.0, -MaxY*fScaleY, 0.0);
158 if( nZAxisOrientation==AxisOrientation_MATHEMATICAL )
159 aMatrix.translate(0.0, 0.0, -MaxZ*fScaleZ);//z direction in draw is reverse mathematical direction
160 else
161 aMatrix.translate(0.0, 0.0, -MinZ*fScaleZ);
163 aMatrix = m_aMatrixScreenToScene*aMatrix;
165 m_xTransformationLogicToScene.reset(new Linear3DTransformation(B3DHomMatrixToHomogenMatrix( aMatrix ), m_bSwapXAndY));
167 return m_xTransformationLogicToScene.get();
170 drawing::Position3D PlottingPositionHelper::transformLogicToScene(
171 double fX, double fY, double fZ, bool bClip ) const
173 doLogicScaling( &fX,&fY,&fZ );
174 if(bClip)
175 clipScaledLogicValues( &fX,&fY,&fZ );
177 return transformScaledLogicToScene( fX, fY, fZ, false );
180 drawing::Position3D PlottingPositionHelper::transformScaledLogicToScene(
181 double fX, double fY, double fZ, bool bClip ) const
183 if( bClip )
184 clipScaledLogicValues( &fX,&fY,&fZ );
186 drawing::Position3D aPos( fX, fY, fZ);
188 ::chart::XTransformation2* pTransformation =
189 getTransformationScaledLogicToScene();
190 return pTransformation->transform( aPos );
193 awt::Point PlottingPositionHelper::transformSceneToScreenPosition( const drawing::Position3D& rScenePosition3D
194 , const rtl::Reference<SvxShapeGroupAnyD>& xSceneTarget
195 , sal_Int32 nDimensionCount )
197 //@todo would like to have a cheaper method to do this transformation
198 awt::Point aScreenPoint( static_cast<sal_Int32>(rScenePosition3D.PositionX), static_cast<sal_Int32>(rScenePosition3D.PositionY) );
200 //transformation from scene to screen (only necessary for 3D):
201 if(nDimensionCount==3)
203 //create 3D anchor shape
204 tPropertyNameMap aDummyPropertyNameMap;
205 rtl::Reference<Svx3DExtrudeObject> xShape3DAnchor = ShapeFactory::createCube( xSceneTarget
206 , rScenePosition3D,drawing::Direction3D(1,1,1)
207 , 0, nullptr, aDummyPropertyNameMap);
208 //get 2D position from xShape3DAnchor
209 aScreenPoint = xShape3DAnchor->getPosition();
210 xSceneTarget->remove(xShape3DAnchor);
212 return aScreenPoint;
215 void PlottingPositionHelper::transformScaledLogicToScene( drawing::PolyPolygonShape3D& rPolygon ) const
217 drawing::Position3D aScenePosition;
218 auto SequenceXRange = asNonConstRange(rPolygon.SequenceX);
219 auto SequenceYRange = asNonConstRange(rPolygon.SequenceY);
220 auto SequenceZRange = asNonConstRange(rPolygon.SequenceZ);
221 for( sal_Int32 nS = rPolygon.SequenceX.getLength(); nS--;)
223 auto xValuesRange = asNonConstRange(SequenceXRange[nS]);
224 auto yValuesRange = asNonConstRange(SequenceYRange[nS]);
225 auto zValuesRange = asNonConstRange(SequenceZRange[nS]);
226 for( sal_Int32 nP = SequenceXRange[nS].getLength(); nP--; )
228 double& fX = xValuesRange[nP];
229 double& fY = yValuesRange[nP];
230 double& fZ = zValuesRange[nP];
231 aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true );
232 fX = aScenePosition.PositionX;
233 fY = aScenePosition.PositionY;
234 fZ = aScenePosition.PositionZ;
239 void PlottingPositionHelper::transformScaledLogicToScene( std::vector<std::vector<css::drawing::Position3D>>& rPolygon ) const
241 drawing::Position3D aScenePosition;
242 for( sal_Int32 nS = static_cast<sal_Int32>(rPolygon.size()); nS--;)
244 auto valuesRange = rPolygon[nS].data();
245 for( sal_Int32 nP = rPolygon[nS].size(); nP--; )
247 double& fX = valuesRange[nP].PositionX;
248 double& fY = valuesRange[nP].PositionY;
249 double& fZ = valuesRange[nP].PositionZ;
250 aScenePosition = transformScaledLogicToScene( fX,fY,fZ,true );
251 fX = aScenePosition.PositionX;
252 fY = aScenePosition.PositionY;
253 fZ = aScenePosition.PositionZ;
258 void PlottingPositionHelper::clipScaledLogicValues( double* pX, double* pY, double* pZ ) const
260 //get logic clip values:
261 double MinX = getLogicMinX();
262 double MinY = getLogicMinY();
263 double MinZ = getLogicMinZ();
264 double MaxX = getLogicMaxX();
265 double MaxY = getLogicMaxY();
266 double MaxZ = getLogicMaxZ();
268 //apply scaling
269 doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
270 doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
272 if(pX)
274 if( *pX < MinX )
275 *pX = MinX;
276 else if( *pX > MaxX )
277 *pX = MaxX;
279 if(pY)
281 if( *pY < MinY )
282 *pY = MinY;
283 else if( *pY > MaxY )
284 *pY = MaxY;
286 if(pZ)
288 if( *pZ < MinZ )
289 *pZ = MinZ;
290 else if( *pZ > MaxZ )
291 *pZ = MaxZ;
295 basegfx::B2DRectangle PlottingPositionHelper::getScaledLogicClipDoubleRect() const
297 //get logic clip values:
298 double MinX = getLogicMinX();
299 double MinY = getLogicMinY();
300 double MinZ = getLogicMinZ();
301 double MaxX = getLogicMaxX();
302 double MaxY = getLogicMaxY();
303 double MaxZ = getLogicMaxZ();
305 //apply scaling
306 doUnshiftedLogicScaling( &MinX, &MinY, &MinZ );
307 doUnshiftedLogicScaling( &MaxX, &MaxY, &MaxZ);
309 basegfx::B2DRectangle aRet( MinX, MaxY, MaxX, MinY );
310 return aRet;
313 drawing::Direction3D PlottingPositionHelper::getScaledLogicWidth() const
315 drawing::Direction3D aRet;
317 double MinX = getLogicMinX();
318 double MinY = getLogicMinY();
319 double MinZ = getLogicMinZ();
320 double MaxX = getLogicMaxX();
321 double MaxY = getLogicMaxY();
322 double MaxZ = getLogicMaxZ();
324 doLogicScaling( &MinX, &MinY, &MinZ );
325 doLogicScaling( &MaxX, &MaxY, &MaxZ);
327 aRet.DirectionX = MaxX - MinX;
328 aRet.DirectionY = MaxY - MinY;
329 aRet.DirectionZ = MaxZ - MinZ;
330 return aRet;
333 PolarPlottingPositionHelper::PolarPlottingPositionHelper()
334 : m_fRadiusOffset(0.0)
335 , m_fAngleDegreeOffset(90.0)
337 m_bMaySkipPointsInRegressionCalculation = false;
340 PolarPlottingPositionHelper::PolarPlottingPositionHelper( const PolarPlottingPositionHelper& rSource )
341 : PlottingPositionHelper(rSource)
342 , m_fRadiusOffset( rSource.m_fRadiusOffset )
343 , m_fAngleDegreeOffset( rSource.m_fAngleDegreeOffset )
344 , m_aUnitCartesianToScene( rSource.m_aUnitCartesianToScene )
348 PolarPlottingPositionHelper::~PolarPlottingPositionHelper()
352 std::unique_ptr<PlottingPositionHelper> PolarPlottingPositionHelper::clone() const
354 return std::make_unique<PolarPlottingPositionHelper>(*this);
357 void PolarPlottingPositionHelper::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix)
359 PlottingPositionHelper::setTransformationSceneToScreen( rMatrix);
360 m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene );
362 void PolarPlottingPositionHelper::setScales( std::vector< ExplicitScaleData >&& rScales, bool bSwapXAndYAxis )
364 PlottingPositionHelper::setScales( std::move(rScales), bSwapXAndYAxis );
365 m_aUnitCartesianToScene =impl_calculateMatrixUnitCartesianToScene( m_aMatrixScreenToScene );
368 ::basegfx::B3DHomMatrix PolarPlottingPositionHelper::impl_calculateMatrixUnitCartesianToScene( const ::basegfx::B3DHomMatrix& rMatrixScreenToScene ) const
370 ::basegfx::B3DHomMatrix aRet;
372 if( m_aScales.empty() )
373 return aRet;
375 double fTranslate =1.0;
376 double fScale =FIXED_SIZE_FOR_3D_CHART_VOLUME/2.0;
378 double fTranslateLogicZ;
379 double fScaleLogicZ;
381 double fScaleDirectionZ = m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL ? 1.0 : -1.0;
382 double MinZ = getLogicMinZ();
383 double MaxZ = getLogicMaxZ();
384 doLogicScaling( nullptr, nullptr, &MinZ );
385 doLogicScaling( nullptr, nullptr, &MaxZ );
386 double fWidthZ = MaxZ - MinZ;
388 if( m_aScales[2].Orientation==AxisOrientation_MATHEMATICAL )
389 fTranslateLogicZ=MinZ;
390 else
391 fTranslateLogicZ=MaxZ;
392 fScaleLogicZ = fScaleDirectionZ*FIXED_SIZE_FOR_3D_CHART_VOLUME/fWidthZ;
395 double fTranslateX = fTranslate;
396 double fTranslateY = fTranslate;
397 double fTranslateZ = fTranslateLogicZ;
399 double fScaleX = fScale;
400 double fScaleY = fScale;
401 double fScaleZ = fScaleLogicZ;
403 aRet.translate(fTranslateX, fTranslateY, fTranslateZ);//x first
404 aRet.scale(fScaleX, fScaleY, fScaleZ);//x first
406 aRet = rMatrixScreenToScene * aRet;
407 return aRet;
410 ::chart::XTransformation2* PolarPlottingPositionHelper::getTransformationScaledLogicToScene() const
412 if( !m_xTransformationLogicToScene )
413 m_xTransformationLogicToScene.reset(new VPolarTransformation(*this));
414 return m_xTransformationLogicToScene.get();
417 double PolarPlottingPositionHelper::getWidthAngleDegree( double& fStartLogicValueOnAngleAxis, double& fEndLogicValueOnAngleAxis ) const
419 const ExplicitScaleData& rAngleScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0];
420 if( rAngleScale.Orientation != AxisOrientation_MATHEMATICAL )
421 std::swap( fStartLogicValueOnAngleAxis, fEndLogicValueOnAngleAxis );
423 double fStartAngleDegree = transformToAngleDegree( fStartLogicValueOnAngleAxis );
424 double fEndAngleDegree = transformToAngleDegree( fEndLogicValueOnAngleAxis );
425 double fWidthAngleDegree = fEndAngleDegree - fStartAngleDegree;
427 if( ::rtl::math::approxEqual( fStartAngleDegree, fEndAngleDegree )
428 && !::rtl::math::approxEqual( fStartLogicValueOnAngleAxis, fEndLogicValueOnAngleAxis ) )
429 fWidthAngleDegree = 360.0;
431 // tdf#123504: both 0 and 360 are valid and different values here!
432 while (fWidthAngleDegree < 0.0)
433 fWidthAngleDegree += 360.0;
434 while (fWidthAngleDegree > 360.0)
435 fWidthAngleDegree -= 360.0;
437 return fWidthAngleDegree;
440 //This method does a lot of computation for understanding which scale to
441 //utilize and if reverse orientation should be used. Indeed, for a pie or donut,
442 //the final result is as simple as multiplying by 360 and adding
443 //`m_fAngleDegreeOffset`.
444 double PolarPlottingPositionHelper::transformToAngleDegree( double fLogicValueOnAngleAxis, bool bDoScaling ) const
446 double fRet=0.0;
448 double fAxisAngleScaleDirection = 1.0;
450 const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[1] : m_aScales[0];
451 if(rScale.Orientation != AxisOrientation_MATHEMATICAL)
452 fAxisAngleScaleDirection *= -1.0;
455 double MinAngleValue = 0.0;
456 double MaxAngleValue = 0.0;
458 double MinX = getLogicMinX();
459 double MinY = getLogicMinY();
460 double MaxX = getLogicMaxX();
461 double MaxY = getLogicMaxY();
462 double MinZ = getLogicMinZ();
463 double MaxZ = getLogicMaxZ();
465 doLogicScaling( &MinX, &MinY, &MinZ );
466 doLogicScaling( &MaxX, &MaxY, &MaxZ);
468 MinAngleValue = m_bSwapXAndY ? MinY : MinX;
469 MaxAngleValue = m_bSwapXAndY ? MaxY : MaxX;
472 double fScaledLogicAngleValue = 0.0;
473 if(bDoScaling)
475 double fX = m_bSwapXAndY ? getLogicMaxX() : fLogicValueOnAngleAxis;
476 double fY = m_bSwapXAndY ? fLogicValueOnAngleAxis : getLogicMaxY();
477 double fZ = getLogicMaxZ();
478 clipLogicValues( &fX, &fY, &fZ );
479 doLogicScaling( &fX, &fY, &fZ );
480 fScaledLogicAngleValue = m_bSwapXAndY ? fY : fX;
482 else
483 fScaledLogicAngleValue = fLogicValueOnAngleAxis;
485 fRet = m_fAngleDegreeOffset
486 + fAxisAngleScaleDirection*(fScaledLogicAngleValue-MinAngleValue)*360.0
487 /fabs(MaxAngleValue-MinAngleValue);
488 // tdf#123504: both 0 and 360 are valid and different values here!
489 while (fRet > 360.0)
490 fRet -= 360.0;
491 while (fRet < 0)
492 fRet += 360.0;
493 return fRet;
497 * Given a value in the radius axis scale range, it returns, in the simplest
498 * case (that is when `m_fRadiusOffset` is zero), the normalized value; when
499 * `m_fRadiusOffset` is not zero (e.g. as in the case of a donut), the interval
500 * used for normalization is extended by `m_fRadiusOffset`: if the axis
501 * orientation is not reversed the new interval becomes
502 * [scale.Minimum - m_fRadiusOffset, scale.Maximum] else it becomes
503 * [scale.Minimum, scale.Maximum + m_fRadiusOffset].
504 * Pay attention here! For the latter case, since the axis orientation is
505 * reversed, the normalization is reversed too. Indeed, we have
506 * `transformToRadius(scale.Maximum + m_fRadiusOffset) = 0` and
507 * `transformToRadius(scale.Minimum) = 1`.
509 * For a pie chart the radius axis scale range is initialized by the
510 * `getMinimum` and `getMaximum` methods of the `PieChart` object (see notes
511 * for `VCoordinateSystem::prepareAutomaticAxisScaling`).
512 * So we have scale.Minimum = 0.5 (always constant!) and
513 * scale.Maximum = 0.5 + number_of_rings + max_offset
514 * (see notes for `PieChart::getMaxOffset`).
515 * Hence we get the following general formulas for computing normalized inner
516 * and outer radius:
518 * 1- transformToRadius(inner_radius) =
519 * (number_of_rings - (ring_index + 1) + m_fRadiusOffset)
520 * / (number_of_rings + max_offset + m_fRadiusOffset)
522 * 2- transformToRadius(outer_radius) =
523 * (1 + number_of_rings - (ring_index + 1) + m_fRadiusOffset)
524 * / (number_of_rings + max_offset + m_fRadiusOffset).
526 * Here you have to take into account that values for inner and outer radius
527 * are swapped since the radius axis is reversed (See notes for
528 * `PiePositionHelper::getInnerAndOuterRadius`). So indeed inner_radius is
529 * the outer and outer_radius is the inner. Anyway still because of the reverse
530 * orientation, the normalization performed by `transformToRadius` is reversed
531 * too, as we have seen above. Hence `transformToRadius(inner_radius)` is
532 * really the normalized inner radius and `transformToRadius(outer_radius)` is
533 * really the normalized outer radius.
535 * Some basic examples where we apply the above formulas:
536 * 1- For a non-exploded pie chart we have:
537 * `transformToRadius(inner_radius) = 0`,
538 * `transformToRadius(outer_radius) = 1`.
539 * 2- For a non-exploded donut with a single ring we have:
540 * `transformToRadius(inner_radius) =
541 * m_fRadiusOffset/(1 + m_fRadiusOffset)`,
542 * `transformToRadius(outer_radius) =
543 * (1 + m_fRadiusOffset)/(1 + m_fRadiusOffset) = 1`.
544 * 3- For an exploded pie chart we have:
545 * `transformToRadius(inner_radius) = 0/(1 + max_offset) = 0`,
546 * `transformToRadius(outer_radius) = 1/(1 + max_offset)`.
548 * The third example needs some remark. Both the logical inner and outer
549 * radius passed to `transformToRadius` are offset by `max_offset`.
550 * However the returned normalized values do not contain any (normalized)
551 * offset term at all, otherwise the returned values would be
552 * `max_offset/(1 + max_offset)` and `1`. Hence, for exploded pie/donut,
553 * `transformToRadius` returns the normalized value of radii without any
554 * offset term. These values are smaller than in the non-exploded case by an
555 * amount equals to the value of the normalized maximum offset
556 * (`max_offset/(1 + max_offset)` in the example above). That is due to the
557 * fact that the normalization keeps into account the space needed for the
558 * offset. This is the correct behavior, in fact the offset for the current
559 * slice could be different from the maximum offset.
560 * These remarks should clarify why the `PieChart::createDataPoint` and
561 * `PieChart::createTextLabelShape` methods add the normalized offset (for the
562 * current slice) to the normalized radii in order to achieve the correct
563 * placement of slice and text shapes.
565 double PolarPlottingPositionHelper::transformToRadius( double fLogicValueOnRadiusAxis, bool bDoScaling ) const
567 double fNormalRadius = 0.0;
569 double fScaledLogicRadiusValue = 0.0;
570 double fX = m_bSwapXAndY ? fLogicValueOnRadiusAxis: getLogicMaxX();
571 double fY = m_bSwapXAndY ? getLogicMaxY() : fLogicValueOnRadiusAxis;
572 if(bDoScaling)
573 doLogicScaling( &fX, &fY, nullptr );
575 fScaledLogicRadiusValue = m_bSwapXAndY ? fX : fY;
577 bool bMinIsInnerRadius = true;
578 const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1];
579 if(rScale.Orientation != AxisOrientation_MATHEMATICAL)
580 bMinIsInnerRadius = false;
582 double fInnerScaledLogicRadius=0.0;
583 double fOuterScaledLogicRadius=0.0;
585 double MinX = getLogicMinX();
586 double MinY = getLogicMinY();
587 doLogicScaling( &MinX, &MinY, nullptr );
588 double MaxX = getLogicMaxX();
589 double MaxY = getLogicMaxY();
590 doLogicScaling( &MaxX, &MaxY, nullptr );
592 double fMin = m_bSwapXAndY ? MinX : MinY;
593 double fMax = m_bSwapXAndY ? MaxX : MaxY;
595 fInnerScaledLogicRadius = bMinIsInnerRadius ? fMin : fMax;
596 fOuterScaledLogicRadius = bMinIsInnerRadius ? fMax : fMin;
599 if( bMinIsInnerRadius )
600 fInnerScaledLogicRadius -= fabs(m_fRadiusOffset);
601 else
602 fInnerScaledLogicRadius += fabs(m_fRadiusOffset);
603 fNormalRadius = (fScaledLogicRadiusValue-fInnerScaledLogicRadius)/(fOuterScaledLogicRadius-fInnerScaledLogicRadius);
605 return fNormalRadius;
608 drawing::Position3D PolarPlottingPositionHelper::transformLogicToScene( double fX, double fY, double fZ, bool bClip ) const
610 if(bClip)
611 clipLogicValues( &fX,&fY,&fZ );
612 double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX;
613 double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY;
614 return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ );
617 drawing::Position3D PolarPlottingPositionHelper::transformScaledLogicToScene( double fX, double fY, double fZ, bool bClip ) const
619 if(bClip)
620 clipScaledLogicValues( &fX,&fY,&fZ );
621 double fLogicValueOnAngleAxis = m_bSwapXAndY ? fY : fX;
622 double fLogicValueOnRadiusAxis = m_bSwapXAndY ? fX : fY;
623 return transformAngleRadiusToScene( fLogicValueOnAngleAxis, fLogicValueOnRadiusAxis, fZ, false );
625 drawing::Position3D PolarPlottingPositionHelper::transformUnitCircleToScene(
626 double fUnitAngleDegree, double fUnitRadius,
627 double fLogicZ ,
628 const ::basegfx::B3DVector& aOffset) const
630 double fAnglePi = basegfx::deg2rad(fUnitAngleDegree);
632 double fX=fUnitRadius*std::cos(fAnglePi);
633 double fY=fUnitRadius*std::sin(fAnglePi);
634 double fZ=fLogicZ;
636 //!! applying matrix to vector does ignore translation, so it is important to use a B3DPoint here instead of B3DVector
637 ::basegfx::B3DPoint aPoint(fX,fY,fZ);
638 aPoint += aOffset;
639 ::basegfx::B3DPoint aRet = m_aUnitCartesianToScene * aPoint;
640 return B3DPointToPosition3D(aRet);
643 drawing::Position3D PolarPlottingPositionHelper::transformAngleRadiusToScene( double fLogicValueOnAngleAxis, double fLogicValueOnRadiusAxis, double fLogicZ, bool bDoScaling ) const
645 double fUnitAngleDegree = transformToAngleDegree(fLogicValueOnAngleAxis,bDoScaling);
646 double fUnitRadius = transformToRadius(fLogicValueOnRadiusAxis,bDoScaling);
648 return transformUnitCircleToScene( fUnitAngleDegree, fUnitRadius, fLogicZ );
651 double PolarPlottingPositionHelper::getOuterLogicRadius() const
653 const ExplicitScaleData& rScale = m_bSwapXAndY ? m_aScales[0] : m_aScales[1];
654 if( rScale.Orientation==AxisOrientation_MATHEMATICAL )
655 return rScale.Maximum;
656 else
657 return rScale.Minimum;
660 bool PlottingPositionHelper::isPercentY() const
662 return m_aScales[1].AxisType==AxisType::PERCENT;
665 double PlottingPositionHelper::getBaseValueY() const
667 return m_aScales[1].Origin;
670 void PlottingPositionHelper::setTimeResolution( tools::Long nTimeResolution, const Date& rNullDate )
672 m_nTimeResolution = nTimeResolution;
673 m_aNullDate = rNullDate;
675 //adapt category width
676 double fCategoryWidth = 1.0;
677 if( !m_aScales.empty() )
679 if( m_aScales[0].AxisType == css::chart2::AxisType::DATE )
681 m_bDateAxis = true;
682 if( nTimeResolution == css::chart::TimeUnit::YEAR )
684 const double fMonthCount = 12.0;//todo: this depends on the DateScaling and must be adjusted in case we use more generic calendars in future
685 fCategoryWidth = fMonthCount;
689 setScaledCategoryWidth(fCategoryWidth);
692 void PlottingPositionHelper::setScaledCategoryWidth( double fScaledCategoryWidth )
694 m_fScaledCategoryWidth = fScaledCategoryWidth;
696 void PlottingPositionHelper::AllowShiftXAxisPos( bool bAllowShift )
698 m_bAllowShiftXAxisPos = bAllowShift;
700 void PlottingPositionHelper::AllowShiftZAxisPos( bool bAllowShift )
702 m_bAllowShiftZAxisPos = bAllowShift;
707 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */