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 "Tickmarks_Equidistant.hxx"
21 #include <rtl/math.hxx>
22 #include <osl/diagnose.h>
30 using namespace ::com::sun::star
;
31 using namespace ::com::sun::star::chart2
;
32 using namespace ::rtl::math
;
35 double EquidistantTickFactory::getMinimumAtIncrement( double fMin
, const ExplicitIncrementData
& rIncrement
)
37 //the returned value will be <= fMin and on a Major Tick given by rIncrement
38 if(rIncrement
.Distance
<=0.0)
41 double fRet
= rIncrement
.BaseValue
+
42 floor( approxSub( fMin
, rIncrement
.BaseValue
)
43 / rIncrement
.Distance
)
48 if( !approxEqual(fRet
, fMin
) )
49 fRet
-= rIncrement
.Distance
;
54 double EquidistantTickFactory::getMaximumAtIncrement( double fMax
, const ExplicitIncrementData
& rIncrement
)
56 //the returned value will be >= fMax and on a Major Tick given by rIncrement
57 if(rIncrement
.Distance
<=0.0)
60 double fRet
= rIncrement
.BaseValue
+
61 floor( approxSub( fMax
, rIncrement
.BaseValue
)
62 / rIncrement
.Distance
)
67 if( !approxEqual(fRet
, fMax
) )
68 fRet
+= rIncrement
.Distance
;
73 EquidistantTickFactory::EquidistantTickFactory(
74 ExplicitScaleData aScale
, ExplicitIncrementData aIncrement
)
75 : m_rScale(std::move( aScale
))
76 , m_rIncrement(std::move( aIncrement
))
78 //@todo: make sure that the scale is valid for the scaling
80 m_pfCurrentValues
.reset( new double[getTickDepth()] );
82 if( m_rScale
.Scaling
.is() )
84 m_xInverseScaling
= m_rScale
.Scaling
->getInverseScaling();
85 OSL_ENSURE( m_xInverseScaling
.is(), "each Scaling needs to return an inverse Scaling" );
88 double fMin
= m_fScaledVisibleMin
= m_rScale
.Minimum
;
89 if( m_xInverseScaling
.is() )
91 m_fScaledVisibleMin
= m_rScale
.Scaling
->doScaling(m_fScaledVisibleMin
);
92 if(m_rIncrement
.PostEquidistant
)
93 fMin
= m_fScaledVisibleMin
;
96 double fMax
= m_fScaledVisibleMax
= m_rScale
.Maximum
;
97 if( m_xInverseScaling
.is() )
99 m_fScaledVisibleMax
= m_rScale
.Scaling
->doScaling(m_fScaledVisibleMax
);
100 if(m_rIncrement
.PostEquidistant
)
101 fMax
= m_fScaledVisibleMax
;
104 m_fOuterMajorTickBorderMin
= EquidistantTickFactory::getMinimumAtIncrement( fMin
, m_rIncrement
);
105 m_fOuterMajorTickBorderMax
= EquidistantTickFactory::getMaximumAtIncrement( fMax
, m_rIncrement
);
107 m_fOuterMajorTickBorderMin_Scaled
= m_fOuterMajorTickBorderMin
;
108 m_fOuterMajorTickBorderMax_Scaled
= m_fOuterMajorTickBorderMax
;
109 if(m_rIncrement
.PostEquidistant
|| !m_xInverseScaling
.is())
112 m_fOuterMajorTickBorderMin_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMin
);
113 m_fOuterMajorTickBorderMax_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMax
);
115 //check validity of new range: m_fOuterMajorTickBorderMin <-> m_fOuterMajorTickBorderMax
116 //it is assumed here, that the original range in the given Scale is valid
117 if( !std::isfinite(m_fOuterMajorTickBorderMin_Scaled
) )
119 m_fOuterMajorTickBorderMin
+= m_rIncrement
.Distance
;
120 m_fOuterMajorTickBorderMin_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMin
);
122 if( !std::isfinite(m_fOuterMajorTickBorderMax_Scaled
) )
124 m_fOuterMajorTickBorderMax
-= m_rIncrement
.Distance
;
125 m_fOuterMajorTickBorderMax_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMax
);
129 EquidistantTickFactory::~EquidistantTickFactory()
133 sal_Int32
EquidistantTickFactory::getTickDepth() const
135 return static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size()) + 1;
138 void EquidistantTickFactory::addSubTicks( sal_Int32 nDepth
, uno::Sequence
< uno::Sequence
< double > >& rParentTicks
) const
140 EquidistantTickIter
aIter( rParentTicks
, m_rIncrement
, nDepth
-1 );
141 double* pfNextParentTick
= aIter
.firstValue();
142 if(!pfNextParentTick
)
144 double fLastParentTick
= *pfNextParentTick
;
145 pfNextParentTick
= aIter
.nextValue();
146 if(!pfNextParentTick
)
149 sal_Int32 nMaxSubTickCount
= getMaxTickCount( nDepth
);
150 if(!nMaxSubTickCount
)
153 uno::Sequence
< double > aSubTicks(nMaxSubTickCount
);
154 auto pSubTicks
= aSubTicks
.getArray();
155 sal_Int32 nRealSubTickCount
= 0;
156 sal_Int32 nIntervalCount
= m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
;
158 double* pValue
= nullptr;
159 for(; pfNextParentTick
; fLastParentTick
=*pfNextParentTick
, pfNextParentTick
= aIter
.nextValue())
161 for( sal_Int32 nPartTick
= 1; nPartTick
<nIntervalCount
; nPartTick
++ )
163 pValue
= getMinorTick( nPartTick
, nDepth
164 , fLastParentTick
, *pfNextParentTick
);
168 pSubTicks
[nRealSubTickCount
] = *pValue
;
173 aSubTicks
.realloc(nRealSubTickCount
);
174 rParentTicks
.getArray()[nDepth
] = std::move(aSubTicks
);
175 if(static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size())>nDepth
)
176 addSubTicks( nDepth
+1, rParentTicks
);
179 sal_Int32
EquidistantTickFactory::getMaxTickCount( sal_Int32 nDepth
) const
181 //return the maximum amount of ticks
182 //possibly open intervals at the two ends of the region are handled as if they were completely visible
183 //(this is necessary for calculating the sub ticks at the borders correctly)
185 if( nDepth
>= getTickDepth() )
187 if( m_fOuterMajorTickBorderMax
< m_fOuterMajorTickBorderMin
)
189 if( m_rIncrement
.Distance
<=0.0)
193 if(m_rIncrement
.PostEquidistant
)
194 fSub
= approxSub( m_fScaledVisibleMax
, m_fScaledVisibleMin
);
196 fSub
= approxSub( m_rScale
.Maximum
, m_rScale
.Minimum
);
198 if (!std::isfinite(fSub
))
201 double fIntervalCount
= fSub
/ m_rIncrement
.Distance
;
202 if (fIntervalCount
> std::numeric_limits
<sal_Int32
>::max())
203 // Interval count too high! Bail out.
206 sal_Int32 nIntervalCount
= static_cast<sal_Int32
>(fIntervalCount
);
209 for(sal_Int32 nN
=0; nN
<nDepth
-1; nN
++)
211 if( m_rIncrement
.SubIncrements
[nN
].IntervalCount
>1 )
212 nIntervalCount
*= m_rIncrement
.SubIncrements
[nN
].IntervalCount
;
215 sal_Int32 nTickCount
= nIntervalCount
;
216 if(nDepth
>0 && m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
>1)
217 nTickCount
= nIntervalCount
* (m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
-1);
222 double* EquidistantTickFactory::getMajorTick( sal_Int32 nTick
) const
224 m_pfCurrentValues
[0] = m_fOuterMajorTickBorderMin
+ nTick
*m_rIncrement
.Distance
;
226 if(m_pfCurrentValues
[0]>m_fOuterMajorTickBorderMax
)
228 if( !approxEqual(m_pfCurrentValues
[0],m_fOuterMajorTickBorderMax
) )
231 if(m_pfCurrentValues
[0]<m_fOuterMajorTickBorderMin
)
233 if( !approxEqual(m_pfCurrentValues
[0],m_fOuterMajorTickBorderMin
) )
237 //return always the value after scaling
238 if(!m_rIncrement
.PostEquidistant
&& m_xInverseScaling
.is() )
239 m_pfCurrentValues
[0] = m_rScale
.Scaling
->doScaling( m_pfCurrentValues
[0] );
241 return &m_pfCurrentValues
[0];
244 double* EquidistantTickFactory::getMinorTick( sal_Int32 nTick
, sal_Int32 nDepth
245 , double fStartParentTick
, double fNextParentTick
) const
247 //check validity of arguments
249 //OSL_ENSURE( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick");
250 if(fStartParentTick
>= fNextParentTick
)
252 if(nDepth
>static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size()) || nDepth
<=0)
255 //subticks are only calculated if they are laying between parent ticks:
258 if(nTick
>=m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
)
262 bool bPostEquidistant
= m_rIncrement
.SubIncrements
[nDepth
-1].PostEquidistant
;
264 double fAdaptedStartParent
= fStartParentTick
;
265 double fAdaptedNextParent
= fNextParentTick
;
267 if( !bPostEquidistant
&& m_xInverseScaling
.is() )
269 fAdaptedStartParent
= m_xInverseScaling
->doScaling(fStartParentTick
);
270 fAdaptedNextParent
= m_xInverseScaling
->doScaling(fNextParentTick
);
273 double fDistance
= (fAdaptedNextParent
- fAdaptedStartParent
)/m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
;
275 m_pfCurrentValues
[nDepth
] = fAdaptedStartParent
+ nTick
*fDistance
;
277 //return always the value after scaling
278 if(!bPostEquidistant
&& m_xInverseScaling
.is() )
279 m_pfCurrentValues
[nDepth
] = m_rScale
.Scaling
->doScaling( m_pfCurrentValues
[nDepth
] );
281 if( !isWithinOuterBorder( m_pfCurrentValues
[nDepth
] ) )
284 return &m_pfCurrentValues
[nDepth
];
287 bool EquidistantTickFactory::isWithinOuterBorder( double fScaledValue
) const
289 if(fScaledValue
>m_fOuterMajorTickBorderMax_Scaled
)
291 if(fScaledValue
<m_fOuterMajorTickBorderMin_Scaled
)
297 bool EquidistantTickFactory::isVisible( double fScaledValue
) const
299 if(fScaledValue
>m_fScaledVisibleMax
)
301 if( !approxEqual(fScaledValue
,m_fScaledVisibleMax
) )
304 if(fScaledValue
<m_fScaledVisibleMin
)
306 if( !approxEqual(fScaledValue
,m_fScaledVisibleMin
) )
312 void EquidistantTickFactory::getAllTicks( TickInfoArraysType
& rAllTickInfos
) const
314 //create point sequences for each tick depth
315 const sal_Int32 nDepthCount
= getTickDepth();
316 const sal_Int32 nMaxMajorTickCount
= getMaxTickCount(0);
318 if (nDepthCount
<= 0 || nMaxMajorTickCount
<= 0)
321 uno::Sequence
< uno::Sequence
< double > > aAllTicks(nDepthCount
);
322 auto pAllTicks
= aAllTicks
.getArray();
323 pAllTicks
[0].realloc(nMaxMajorTickCount
);
324 auto pAllTicks0
= pAllTicks
[0].getArray();
326 sal_Int32 nRealMajorTickCount
= 0;
327 for( sal_Int32 nMajorTick
=0; nMajorTick
<nMaxMajorTickCount
; nMajorTick
++ )
329 double* pValue
= getMajorTick( nMajorTick
);
332 pAllTicks0
[nRealMajorTickCount
] = *pValue
;
333 nRealMajorTickCount
++;
335 if(!nRealMajorTickCount
)
337 pAllTicks
[0].realloc(nRealMajorTickCount
);
339 addSubTicks(1, aAllTicks
);
341 //so far we have added all ticks between the outer major tick marks
342 //this was necessary to create sub ticks correctly
343 //now we reduce all ticks to the visible ones that lie between the real borders
344 sal_Int32 nDepth
= 0;
346 for( nDepth
= 0; nDepth
< nDepthCount
; nDepth
++)
348 sal_Int32 nInvisibleAtLowerBorder
= 0;
349 sal_Int32 nInvisibleAtUpperBorder
= 0;
350 //we need only to check all ticks within the first major interval at each border
351 sal_Int32 nCheckCount
= 1;
352 for(sal_Int32 nN
=0; nN
<nDepth
; nN
++)
354 if( m_rIncrement
.SubIncrements
[nN
].IntervalCount
>1 )
355 nCheckCount
*= m_rIncrement
.SubIncrements
[nN
].IntervalCount
;
357 uno::Sequence
< double >& rTicks
= pAllTicks
[nDepth
];
358 sal_Int32 nCount
= rTicks
.getLength();
360 for( nTick
=0; nTick
<nCheckCount
&& nTick
<nCount
; nTick
++)
362 if( !isVisible( rTicks
[nTick
] ) )
363 nInvisibleAtLowerBorder
++;
366 for( nTick
=nCount
-1; nTick
>nCount
-1-nCheckCount
&& nTick
>=0; nTick
--)
368 if( !isVisible( rTicks
[nTick
] ) )
369 nInvisibleAtUpperBorder
++;
372 if( !nInvisibleAtLowerBorder
&& !nInvisibleAtUpperBorder
)
374 if( !nInvisibleAtLowerBorder
)
375 rTicks
.realloc(nCount
-nInvisibleAtUpperBorder
);
378 sal_Int32 nNewCount
= nCount
-nInvisibleAtUpperBorder
-nInvisibleAtLowerBorder
;
382 uno::Sequence
< double > aOldTicks(rTicks
);
383 rTicks
.realloc(nNewCount
);
384 auto pTicks
= rTicks
.getArray();
385 for(nTick
= 0; nTick
<nNewCount
; nTick
++)
386 pTicks
[nTick
] = aOldTicks
[nInvisibleAtLowerBorder
+nTick
];
391 rAllTickInfos
.resize(aAllTicks
.getLength());
392 for( nDepth
=0 ;nDepth
<aAllTicks
.getLength(); nDepth
++ )
394 sal_Int32 nCount
= aAllTicks
[nDepth
].getLength();
396 TickInfoArrayType
& rTickInfoVector
= rAllTickInfos
[nDepth
];
397 rTickInfoVector
.clear();
398 rTickInfoVector
.reserve( nCount
);
399 for(sal_Int32 nN
= 0; nN
<nCount
; nN
++)
401 TickInfo
aTickInfo(m_xInverseScaling
);
402 aTickInfo
.fScaledTickValue
= aAllTicks
[nDepth
][nN
];
403 rTickInfoVector
.push_back(aTickInfo
);
408 void EquidistantTickFactory::getAllTicksShifted( TickInfoArraysType
& rAllTickInfos
) const
410 ExplicitIncrementData
aShiftedIncrement( m_rIncrement
);
411 aShiftedIncrement
.BaseValue
= m_rIncrement
.BaseValue
-m_rIncrement
.Distance
/2.0;
412 EquidistantTickFactory( m_rScale
, std::move(aShiftedIncrement
) ).getAllTicks(rAllTickInfos
);
415 EquidistantTickIter::EquidistantTickIter( const uno::Sequence
< uno::Sequence
< double > >& rTicks
416 , const ExplicitIncrementData
& rIncrement
417 , sal_Int32 nMaxDepth
)
418 : m_pSimpleTicks(&rTicks
)
419 , m_pInfoTicks(nullptr)
420 , m_rIncrement(rIncrement
)
423 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
425 initIter( nMaxDepth
);
428 EquidistantTickIter::EquidistantTickIter( TickInfoArraysType
& rTicks
429 , const ExplicitIncrementData
& rIncrement
430 , sal_Int32 nMaxDepth
)
431 : m_pSimpleTicks(nullptr)
432 , m_pInfoTicks(&rTicks
)
433 , m_rIncrement(rIncrement
)
436 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
438 initIter( nMaxDepth
);
441 void EquidistantTickIter::initIter( sal_Int32 nMaxDepth
)
443 m_nMaxDepth
= nMaxDepth
;
444 if(nMaxDepth
<0 || m_nMaxDepth
>getMaxDepth())
445 m_nMaxDepth
=getMaxDepth();
447 sal_Int32 nDepth
= 0;
448 for( nDepth
= 0; nDepth
<=m_nMaxDepth
;nDepth
++ )
449 m_nTickCount
+= getTickCount(nDepth
);
454 m_pnPositions
.reset( new sal_Int32
[m_nMaxDepth
+1] );
456 m_pnPreParentCount
.reset( new sal_Int32
[m_nMaxDepth
+1] );
457 m_pbIntervalFinished
.reset( new bool[m_nMaxDepth
+1] );
458 m_pnPreParentCount
[0] = 0;
459 m_pbIntervalFinished
[0] = false;
460 double fParentValue
= getTickValue(0,0);
461 for( nDepth
= 1; nDepth
<=m_nMaxDepth
;nDepth
++ )
463 m_pbIntervalFinished
[nDepth
] = false;
465 sal_Int32 nPreParentCount
= 0;
466 sal_Int32 nCount
= getTickCount(nDepth
);
467 for(sal_Int32 nN
= 0; nN
<nCount
; nN
++)
469 if(getTickValue(nDepth
,nN
) < fParentValue
)
474 m_pnPreParentCount
[nDepth
] = nPreParentCount
;
477 double fNextParentValue
= getTickValue(nDepth
,0);
478 if( fNextParentValue
< fParentValue
)
479 fParentValue
= fNextParentValue
;
484 EquidistantTickIter::~EquidistantTickIter()
488 sal_Int32
EquidistantTickIter::getStartDepth() const
490 //find the depth of the first visible tickmark:
491 //it is the depth of the smallest value
492 sal_Int32 nReturnDepth
=0;
493 double fMinValue
= DBL_MAX
;
494 for(sal_Int32 nDepth
= 0; nDepth
<=m_nMaxDepth
;nDepth
++ )
496 sal_Int32 nCount
= getTickCount(nDepth
);
499 double fThisValue
= getTickValue(nDepth
,0);
500 if(fThisValue
<fMinValue
)
502 nReturnDepth
= nDepth
;
503 fMinValue
= fThisValue
;
509 double* EquidistantTickIter::firstValue()
513 m_fCurrentValue
= getTickValue(m_nCurrentDepth
, m_pnPositions
[m_nCurrentDepth
]);
514 return &m_fCurrentValue
;
519 TickInfo
* EquidistantTickIter::firstInfo()
521 if( m_pInfoTicks
&& gotoFirst() )
522 return &(*m_pInfoTicks
)[m_nCurrentDepth
][m_pnPositions
[m_nCurrentDepth
]];
526 sal_Int32
EquidistantTickIter::getIntervalCount( sal_Int32 nDepth
)
528 if(nDepth
>static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size()) || nDepth
<0)
534 return m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
;
537 bool EquidistantTickIter::isAtLastPartTick()
541 sal_Int32 nIntervalCount
= getIntervalCount( m_nCurrentDepth
);
542 if(!nIntervalCount
|| nIntervalCount
== 1)
544 if( m_pbIntervalFinished
[m_nCurrentDepth
] )
546 sal_Int32 nPos
= m_pnPositions
[m_nCurrentDepth
]+1;
547 if(m_pnPreParentCount
[m_nCurrentDepth
])
548 nPos
+= nIntervalCount
-1 - m_pnPreParentCount
[m_nCurrentDepth
];
549 bool bRet
= nPos
&& nPos
% (nIntervalCount
-1) == 0;
550 if(!nPos
&& !m_pnPreParentCount
[m_nCurrentDepth
]
551 && m_pnPositions
[m_nCurrentDepth
-1]==-1 )
556 bool EquidistantTickIter::gotoFirst()
563 for(sal_Int32 nDepth
= 0; nDepth
<=m_nMaxDepth
;nDepth
++ )
564 m_pnPositions
[nDepth
] = -1;
567 m_nCurrentDepth
= getStartDepth();
568 m_pnPositions
[m_nCurrentDepth
] = 0;
572 bool EquidistantTickIter::gotoNext()
574 if( m_nCurrentPos
< 0 )
578 if( m_nCurrentPos
>= m_nTickCount
)
581 if( m_nCurrentDepth
==m_nMaxDepth
&& isAtLastPartTick() )
585 m_pbIntervalFinished
[m_nCurrentDepth
] = true;
588 while( m_nCurrentDepth
&& isAtLastPartTick() );
590 else if( m_nCurrentDepth
<m_nMaxDepth
)
596 while( m_nCurrentDepth
<m_nMaxDepth
);
598 m_pbIntervalFinished
[m_nCurrentDepth
] = false;
599 m_pnPositions
[m_nCurrentDepth
] = m_pnPositions
[m_nCurrentDepth
]+1;
603 double* EquidistantTickIter::nextValue()
607 m_fCurrentValue
= getTickValue(m_nCurrentDepth
, m_pnPositions
[m_nCurrentDepth
]);
608 return &m_fCurrentValue
;
613 TickInfo
* EquidistantTickIter::nextInfo()
615 if( m_pInfoTicks
&& gotoNext() &&
616 static_cast< sal_Int32
>(
617 (*m_pInfoTicks
)[m_nCurrentDepth
].size()) > m_pnPositions
[m_nCurrentDepth
] )
619 return &(*m_pInfoTicks
)[m_nCurrentDepth
][m_pnPositions
[m_nCurrentDepth
]];
626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */