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/DoubleSequence.hpp>
31 #include <com/sun/star/drawing/Position3D.hpp>
32 #include <com/sun/star/drawing/XShapes.hpp>
34 #include <rtl/math.hxx>
38 using namespace ::com::sun::star
;
39 using namespace ::com::sun::star::chart2
;
41 PlottingPositionHelper::PlottingPositionHelper()
43 , m_aMatrixScreenToScene()
44 , m_bSwapXAndY( false )
45 , m_nXResolution( 1000 )
46 , m_nYResolution( 1000 )
47 , m_nZResolution( 1000 )
48 , m_bMaySkipPointsInRegressionCalculation( true )
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
)
88 pRet
->m_aScales
[1]=rSecondaryScale
;
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
)
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
;
128 doUnshiftedLogicScaling( &MinX
, &MinY
, &MinZ
);
129 doUnshiftedLogicScaling( &MaxX
, &MaxY
, &MaxZ
);
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);
155 aMatrix
.translate(-MaxX
*fScaleX
, 0.0, 0.0);
156 if( nYAxisOrientation
==AxisOrientation_MATHEMATICAL
)
157 aMatrix
.translate(0.0, -MinY
*fScaleY
, 0.0);
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
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
);
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
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
);
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();
252 doUnshiftedLogicScaling( &MinX
, &MinY
, &MinZ
);
253 doUnshiftedLogicScaling( &MaxX
, &MaxY
, &MaxZ
);
259 else if( *pX
> MaxX
)
266 else if( *pY
> MaxY
)
273 else if( *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();
289 doUnshiftedLogicScaling( &MinX
, &MinY
, &MinZ
);
290 doUnshiftedLogicScaling( &MaxX
, &MaxY
, &MaxZ
);
292 basegfx::B2DRectangle
aRet( MinX
, MaxY
, MaxX
, MinY
);
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
;
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() )
359 double fTranslate
=1.0;
360 double fScale
=FIXED_SIZE_FOR_3D_CHART_VOLUME
/2.0;
362 double fTranslateLogicZ
;
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
;
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
;
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
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;
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
;
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!
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
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
;
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
);
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
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
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
);
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
;
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
)
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: */