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 <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>
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 )
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
)
86 pRet
->m_aScales
[1]=rSecondaryScale
;
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
;
126 doUnshiftedLogicScaling( &MinX
, &MinY
, &MinZ
);
127 doUnshiftedLogicScaling( &MaxX
, &MaxY
, &MaxZ
);
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);
153 aMatrix
.translate(-MaxX
*fScaleX
, 0.0, 0.0);
154 if( nYAxisOrientation
==AxisOrientation_MATHEMATICAL
)
155 aMatrix
.translate(0.0, -MinY
*fScaleY
, 0.0);
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
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
);
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
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
);
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();
269 doUnshiftedLogicScaling( &MinX
, &MinY
, &MinZ
);
270 doUnshiftedLogicScaling( &MaxX
, &MaxY
, &MaxZ
);
276 else if( *pX
> MaxX
)
283 else if( *pY
> MaxY
)
290 else if( *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();
306 doUnshiftedLogicScaling( &MinX
, &MinY
, &MinZ
);
307 doUnshiftedLogicScaling( &MaxX
, &MaxY
, &MaxZ
);
309 basegfx::B2DRectangle
aRet( MinX
, MaxY
, MaxX
, MinY
);
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
;
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() )
375 double fTranslate
=1.0;
376 double fScale
=FIXED_SIZE_FOR_3D_CHART_VOLUME
/2.0;
378 double fTranslateLogicZ
;
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
;
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
;
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
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;
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
;
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!
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
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
;
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
);
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
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
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
,
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
);
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
);
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
;
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
)
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: */