1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include "Tickmarks_Equidistant.hxx"
21 #include <rtl/math.hxx>
22 #include <osl/diagnose.h>
23 #include <float.h>
24 
25 #include <limits>
26 
27 namespace chart
28 {
29 using namespace ::com::sun::star;
30 using namespace ::com::sun::star::chart2;
31 using namespace ::rtl::math;
32 
33 //static
getMinimumAtIncrement(double fMin,const ExplicitIncrementData & rIncrement)34 double EquidistantTickFactory::getMinimumAtIncrement( double fMin, const ExplicitIncrementData& rIncrement )
35 {
36     //the returned value will be <= fMin and on a Major Tick given by rIncrement
37     if(rIncrement.Distance<=0.0)
38         return fMin;
39 
40     double fRet = rIncrement.BaseValue +
41         floor( approxSub( fMin, rIncrement.BaseValue )
42                     / rIncrement.Distance)
43             *rIncrement.Distance;
44 
45     if( fRet > fMin )
46     {
47         if( !approxEqual(fRet, fMin) )
48             fRet -= rIncrement.Distance;
49     }
50     return fRet;
51 }
52 //static
getMaximumAtIncrement(double fMax,const ExplicitIncrementData & rIncrement)53 double EquidistantTickFactory::getMaximumAtIncrement( double fMax, const ExplicitIncrementData& rIncrement )
54 {
55     //the returned value will be >= fMax and on a Major Tick given by rIncrement
56     if(rIncrement.Distance<=0.0)
57         return fMax;
58 
59     double fRet = rIncrement.BaseValue +
60         floor( approxSub( fMax, rIncrement.BaseValue )
61                     / rIncrement.Distance)
62             *rIncrement.Distance;
63 
64     if( fRet < fMax )
65     {
66         if( !approxEqual(fRet, fMax) )
67             fRet += rIncrement.Distance;
68     }
69     return fRet;
70 }
71 
EquidistantTickFactory(const ExplicitScaleData & rScale,const ExplicitIncrementData & rIncrement)72 EquidistantTickFactory::EquidistantTickFactory(
73           const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement )
74             : m_rScale( rScale )
75             , m_rIncrement( rIncrement )
76 {
77     //@todo: make sure that the scale is valid for the scaling
78 
79     m_pfCurrentValues.reset( new double[getTickDepth()] );
80 
81     if( m_rScale.Scaling.is() )
82     {
83         m_xInverseScaling = m_rScale.Scaling->getInverseScaling();
84         OSL_ENSURE( m_xInverseScaling.is(), "each Scaling needs to return an inverse Scaling" );
85     }
86 
87     double fMin = m_fScaledVisibleMin = m_rScale.Minimum;
88     if( m_xInverseScaling.is() )
89     {
90         m_fScaledVisibleMin = m_rScale.Scaling->doScaling(m_fScaledVisibleMin);
91         if(m_rIncrement.PostEquidistant )
92             fMin = m_fScaledVisibleMin;
93     }
94 
95     double fMax = m_fScaledVisibleMax = m_rScale.Maximum;
96     if( m_xInverseScaling.is() )
97     {
98         m_fScaledVisibleMax = m_rScale.Scaling->doScaling(m_fScaledVisibleMax);
99         if(m_rIncrement.PostEquidistant )
100             fMax = m_fScaledVisibleMax;
101     }
102 
103     m_fOuterMajorTickBorderMin = EquidistantTickFactory::getMinimumAtIncrement( fMin, m_rIncrement );
104     m_fOuterMajorTickBorderMax = EquidistantTickFactory::getMaximumAtIncrement( fMax, m_rIncrement );
105 
106     m_fOuterMajorTickBorderMin_Scaled = m_fOuterMajorTickBorderMin;
107     m_fOuterMajorTickBorderMax_Scaled = m_fOuterMajorTickBorderMax;
108     if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() )
109     {
110         m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin);
111         m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax);
112 
113         //check validity of new range: m_fOuterMajorTickBorderMin <-> m_fOuterMajorTickBorderMax
114         //it is assumed here, that the original range in the given Scale is valid
115         if( !rtl::math::isFinite(m_fOuterMajorTickBorderMin_Scaled) )
116         {
117             m_fOuterMajorTickBorderMin += m_rIncrement.Distance;
118             m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin);
119         }
120         if( !rtl::math::isFinite(m_fOuterMajorTickBorderMax_Scaled) )
121         {
122             m_fOuterMajorTickBorderMax -= m_rIncrement.Distance;
123             m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax);
124         }
125     }
126 }
127 
~EquidistantTickFactory()128 EquidistantTickFactory::~EquidistantTickFactory()
129 {
130 }
131 
getTickDepth() const132 sal_Int32 EquidistantTickFactory::getTickDepth() const
133 {
134     return static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) + 1;
135 }
136 
addSubTicks(sal_Int32 nDepth,uno::Sequence<uno::Sequence<double>> & rParentTicks) const137 void EquidistantTickFactory::addSubTicks( sal_Int32 nDepth, uno::Sequence< uno::Sequence< double > >& rParentTicks ) const
138 {
139     EquidistantTickIter aIter( rParentTicks, m_rIncrement, nDepth-1 );
140     double* pfNextParentTick = aIter.firstValue();
141     if(!pfNextParentTick)
142         return;
143     double fLastParentTick = *pfNextParentTick;
144     pfNextParentTick = aIter.nextValue();
145     if(!pfNextParentTick)
146         return;
147 
148     sal_Int32 nMaxSubTickCount = getMaxTickCount( nDepth );
149     if(!nMaxSubTickCount)
150         return;
151 
152     uno::Sequence< double > aSubTicks(nMaxSubTickCount);
153     sal_Int32 nRealSubTickCount = 0;
154     sal_Int32 nIntervalCount = m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
155 
156     double* pValue = nullptr;
157     for(; pfNextParentTick; fLastParentTick=*pfNextParentTick, pfNextParentTick = aIter.nextValue())
158     {
159         for( sal_Int32 nPartTick = 1; nPartTick<nIntervalCount; nPartTick++ )
160         {
161             pValue = getMinorTick( nPartTick, nDepth
162                         , fLastParentTick, *pfNextParentTick );
163             if(!pValue)
164                 continue;
165 
166             aSubTicks[nRealSubTickCount] = *pValue;
167             nRealSubTickCount++;
168         }
169     }
170 
171     aSubTicks.realloc(nRealSubTickCount);
172     rParentTicks[nDepth] = aSubTicks;
173     if(static_cast<sal_Int32>(m_rIncrement.SubIncrements.size())>nDepth)
174         addSubTicks( nDepth+1, rParentTicks );
175 }
176 
getMaxTickCount(sal_Int32 nDepth) const177 sal_Int32 EquidistantTickFactory::getMaxTickCount( sal_Int32 nDepth ) const
178 {
179     //return the maximum amount of ticks
180     //possibly open intervals at the two ends of the region are handled as if they were completely visible
181     //(this is necessary for calculating the sub ticks at the borders correctly)
182 
183     if( nDepth >= getTickDepth() )
184         return 0;
185     if( m_fOuterMajorTickBorderMax < m_fOuterMajorTickBorderMin )
186         return 0;
187     if( m_rIncrement.Distance<=0.0)
188         return 0;
189 
190     double fSub;
191     if(m_rIncrement.PostEquidistant  )
192         fSub = approxSub( m_fScaledVisibleMax, m_fScaledVisibleMin );
193     else
194         fSub = approxSub( m_rScale.Maximum, m_rScale.Minimum );
195 
196     if (!isFinite(fSub))
197         return 0;
198 
199     double fIntervalCount = fSub / m_rIncrement.Distance;
200     if (fIntervalCount > std::numeric_limits<sal_Int32>::max())
201         // Interval count too high!  Bail out.
202         return 0;
203 
204     sal_Int32 nIntervalCount = static_cast<sal_Int32>(fIntervalCount);
205 
206     nIntervalCount+=3;
207     for(sal_Int32 nN=0; nN<nDepth-1; nN++)
208     {
209         if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
210             nIntervalCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
211     }
212 
213     sal_Int32 nTickCount = nIntervalCount;
214     if(nDepth>0 && m_rIncrement.SubIncrements[nDepth-1].IntervalCount>1)
215         nTickCount = nIntervalCount * (m_rIncrement.SubIncrements[nDepth-1].IntervalCount-1);
216 
217     return nTickCount;
218 }
219 
getMajorTick(sal_Int32 nTick) const220 double* EquidistantTickFactory::getMajorTick( sal_Int32 nTick ) const
221 {
222     m_pfCurrentValues[0] = m_fOuterMajorTickBorderMin + nTick*m_rIncrement.Distance;
223 
224     if(m_pfCurrentValues[0]>m_fOuterMajorTickBorderMax)
225     {
226         if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMax) )
227             return nullptr;
228     }
229     if(m_pfCurrentValues[0]<m_fOuterMajorTickBorderMin)
230     {
231         if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMin) )
232             return nullptr;
233     }
234 
235     //return always the value after scaling
236     if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() )
237         m_pfCurrentValues[0] = m_rScale.Scaling->doScaling( m_pfCurrentValues[0] );
238 
239     return &m_pfCurrentValues[0];
240 }
241 
getMinorTick(sal_Int32 nTick,sal_Int32 nDepth,double fStartParentTick,double fNextParentTick) const242 double* EquidistantTickFactory::getMinorTick( sal_Int32 nTick, sal_Int32 nDepth
243                             , double fStartParentTick, double fNextParentTick ) const
244 {
245     //check validity of arguments
246     {
247         //OSL_ENSURE( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick");
248         if(fStartParentTick >= fNextParentTick)
249             return nullptr;
250         if(nDepth>static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) || nDepth<=0)
251             return nullptr;
252 
253         //subticks are only calculated if they are laying between parent ticks:
254         if(nTick<=0)
255             return nullptr;
256         if(nTick>=m_rIncrement.SubIncrements[nDepth-1].IntervalCount)
257             return nullptr;
258     }
259 
260     bool    bPostEquidistant = m_rIncrement.SubIncrements[nDepth-1].PostEquidistant;
261 
262     double fAdaptedStartParent = fStartParentTick;
263     double fAdaptedNextParent  = fNextParentTick;
264 
265     if( !bPostEquidistant && m_xInverseScaling.is() )
266     {
267         fAdaptedStartParent = m_xInverseScaling->doScaling(fStartParentTick);
268         fAdaptedNextParent  = m_xInverseScaling->doScaling(fNextParentTick);
269     }
270 
271     double fDistance = (fAdaptedNextParent - fAdaptedStartParent)/m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
272 
273     m_pfCurrentValues[nDepth] = fAdaptedStartParent + nTick*fDistance;
274 
275     //return always the value after scaling
276     if(!bPostEquidistant && m_xInverseScaling.is() )
277         m_pfCurrentValues[nDepth] = m_rScale.Scaling->doScaling( m_pfCurrentValues[nDepth] );
278 
279     if( !isWithinOuterBorder( m_pfCurrentValues[nDepth] ) )
280         return nullptr;
281 
282     return &m_pfCurrentValues[nDepth];
283 }
284 
isWithinOuterBorder(double fScaledValue) const285 bool EquidistantTickFactory::isWithinOuterBorder( double fScaledValue ) const
286 {
287     if(fScaledValue>m_fOuterMajorTickBorderMax_Scaled)
288         return false;
289     if(fScaledValue<m_fOuterMajorTickBorderMin_Scaled)
290         return false;
291 
292     return true;
293 }
294 
isVisible(double fScaledValue) const295 bool EquidistantTickFactory::isVisible( double fScaledValue ) const
296 {
297     if(fScaledValue>m_fScaledVisibleMax)
298     {
299         if( !approxEqual(fScaledValue,m_fScaledVisibleMax) )
300             return false;
301     }
302     if(fScaledValue<m_fScaledVisibleMin)
303     {
304         if( !approxEqual(fScaledValue,m_fScaledVisibleMin) )
305             return false;
306     }
307     return true;
308 }
309 
getAllTicks(TickInfoArraysType & rAllTickInfos) const310 void EquidistantTickFactory::getAllTicks( TickInfoArraysType& rAllTickInfos ) const
311 {
312     //create point sequences for each tick depth
313     const sal_Int32 nDepthCount = getTickDepth();
314     const sal_Int32 nMaxMajorTickCount = getMaxTickCount(0);
315 
316     if (nDepthCount <= 0 || nMaxMajorTickCount <= 0)
317         return;
318 
319     uno::Sequence< uno::Sequence< double > > aAllTicks(nDepthCount);
320     aAllTicks[0].realloc(nMaxMajorTickCount);
321 
322     sal_Int32 nRealMajorTickCount = 0;
323     for( sal_Int32 nMajorTick=0; nMajorTick<nMaxMajorTickCount; nMajorTick++ )
324     {
325         double* pValue = getMajorTick( nMajorTick );
326         if(!pValue)
327             continue;
328         aAllTicks[0][nRealMajorTickCount] = *pValue;
329         nRealMajorTickCount++;
330     }
331     if(!nRealMajorTickCount)
332         return;
333     aAllTicks[0].realloc(nRealMajorTickCount);
334 
335     addSubTicks(1, aAllTicks);
336 
337     //so far we have added all ticks between the outer major tick marks
338     //this was necessary to create sub ticks correctly
339     //now we reduce all ticks to the visible ones that lie between the real borders
340     sal_Int32 nDepth = 0;
341     sal_Int32 nTick = 0;
342     for( nDepth = 0; nDepth < nDepthCount; nDepth++)
343     {
344         sal_Int32 nInvisibleAtLowerBorder = 0;
345         sal_Int32 nInvisibleAtUpperBorder = 0;
346         //we need only to check all ticks within the first major interval at each border
347         sal_Int32 nCheckCount = 1;
348         for(sal_Int32 nN=0; nN<nDepth; nN++)
349         {
350             if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
351                 nCheckCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
352         }
353         uno::Sequence< double >& rTicks = aAllTicks[nDepth];
354         sal_Int32 nCount = rTicks.getLength();
355         //check lower border
356         for( nTick=0; nTick<nCheckCount && nTick<nCount; nTick++)
357         {
358             if( !isVisible( rTicks[nTick] ) )
359                 nInvisibleAtLowerBorder++;
360         }
361         //check upper border
362         for( nTick=nCount-1; nTick>nCount-1-nCheckCount && nTick>=0; nTick--)
363         {
364             if( !isVisible( rTicks[nTick] ) )
365                 nInvisibleAtUpperBorder++;
366         }
367         //resize sequence
368         if( !nInvisibleAtLowerBorder && !nInvisibleAtUpperBorder)
369             continue;
370         if( !nInvisibleAtLowerBorder )
371             rTicks.realloc(nCount-nInvisibleAtUpperBorder);
372         else
373         {
374             sal_Int32 nNewCount = nCount-nInvisibleAtUpperBorder-nInvisibleAtLowerBorder;
375             if(nNewCount<0)
376                 nNewCount=0;
377 
378             uno::Sequence< double > aOldTicks(rTicks);
379             rTicks.realloc(nNewCount);
380             for(nTick = 0; nTick<nNewCount; nTick++)
381                 rTicks[nTick] = aOldTicks[nInvisibleAtLowerBorder+nTick];
382         }
383     }
384 
385     //fill return value
386     rAllTickInfos.resize(aAllTicks.getLength());
387     for( nDepth=0 ;nDepth<aAllTicks.getLength(); nDepth++ )
388     {
389         sal_Int32 nCount = aAllTicks[nDepth].getLength();
390 
391         TickInfoArrayType& rTickInfoVector = rAllTickInfos[nDepth];
392         rTickInfoVector.clear();
393         rTickInfoVector.reserve( nCount );
394         for(sal_Int32 nN = 0; nN<nCount; nN++)
395         {
396             TickInfo aTickInfo(m_xInverseScaling);
397             aTickInfo.fScaledTickValue = aAllTicks[nDepth][nN];
398             rTickInfoVector.push_back(aTickInfo);
399         }
400     }
401 }
402 
getAllTicksShifted(TickInfoArraysType & rAllTickInfos) const403 void EquidistantTickFactory::getAllTicksShifted( TickInfoArraysType& rAllTickInfos ) const
404 {
405     ExplicitIncrementData aShiftedIncrement( m_rIncrement );
406     aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0;
407     EquidistantTickFactory( m_rScale, aShiftedIncrement ).getAllTicks(rAllTickInfos);
408 }
409 
EquidistantTickIter(const uno::Sequence<uno::Sequence<double>> & rTicks,const ExplicitIncrementData & rIncrement,sal_Int32 nMaxDepth)410 EquidistantTickIter::EquidistantTickIter( const uno::Sequence< uno::Sequence< double > >& rTicks
411                    , const ExplicitIncrementData& rIncrement
412                    , sal_Int32 nMaxDepth )
413                 : m_pSimpleTicks(&rTicks)
414                 , m_pInfoTicks(nullptr)
415                 , m_rIncrement(rIncrement)
416                 , m_nMaxDepth(0)
417                 , m_nTickCount(0)
418                 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
419 {
420     initIter( nMaxDepth );
421 }
422 
EquidistantTickIter(TickInfoArraysType & rTicks,const ExplicitIncrementData & rIncrement,sal_Int32 nMaxDepth)423 EquidistantTickIter::EquidistantTickIter( TickInfoArraysType& rTicks
424                    , const ExplicitIncrementData& rIncrement
425                    , sal_Int32 nMaxDepth )
426                 : m_pSimpleTicks(nullptr)
427                 , m_pInfoTicks(&rTicks)
428                 , m_rIncrement(rIncrement)
429                 , m_nMaxDepth(0)
430                 , m_nTickCount(0)
431                 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
432 {
433     initIter( nMaxDepth );
434 }
435 
initIter(sal_Int32 nMaxDepth)436 void EquidistantTickIter::initIter( sal_Int32 nMaxDepth )
437 {
438     m_nMaxDepth = nMaxDepth;
439     if(nMaxDepth<0 || m_nMaxDepth>getMaxDepth())
440         m_nMaxDepth=getMaxDepth();
441 
442     sal_Int32 nDepth = 0;
443     for( nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
444         m_nTickCount += getTickCount(nDepth);
445 
446     if(!m_nTickCount)
447         return;
448 
449     m_pnPositions.reset( new sal_Int32[m_nMaxDepth+1] );
450 
451     m_pnPreParentCount.reset( new sal_Int32[m_nMaxDepth+1] );
452     m_pbIntervalFinished.reset( new bool[m_nMaxDepth+1] );
453     m_pnPreParentCount[0] = 0;
454     m_pbIntervalFinished[0] = false;
455     double fParentValue = getTickValue(0,0);
456     for( nDepth = 1; nDepth<=m_nMaxDepth ;nDepth++ )
457     {
458         m_pbIntervalFinished[nDepth] = false;
459 
460         sal_Int32 nPreParentCount = 0;
461         sal_Int32 nCount = getTickCount(nDepth);
462         for(sal_Int32 nN = 0; nN<nCount; nN++)
463         {
464             if(getTickValue(nDepth,nN) < fParentValue)
465                 nPreParentCount++;
466             else
467                 break;
468         }
469         m_pnPreParentCount[nDepth] = nPreParentCount;
470         if(nCount)
471         {
472             double fNextParentValue = getTickValue(nDepth,0);
473             if( fNextParentValue < fParentValue )
474                 fParentValue = fNextParentValue;
475         }
476     }
477 }
478 
~EquidistantTickIter()479 EquidistantTickIter::~EquidistantTickIter()
480 {
481 }
482 
getStartDepth() const483 sal_Int32 EquidistantTickIter::getStartDepth() const
484 {
485     //find the depth of the first visible tickmark:
486     //it is the depth of the smallest value
487     sal_Int32 nReturnDepth=0;
488     double fMinValue = DBL_MAX;
489     for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
490     {
491         sal_Int32 nCount = getTickCount(nDepth);
492         if( !nCount )
493             continue;
494         double fThisValue = getTickValue(nDepth,0);
495         if(fThisValue<fMinValue)
496         {
497             nReturnDepth = nDepth;
498             fMinValue = fThisValue;
499         }
500     }
501     return nReturnDepth;
502 }
503 
firstValue()504 double* EquidistantTickIter::firstValue()
505 {
506     if( gotoFirst() )
507     {
508         m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]);
509         return &m_fCurrentValue;
510     }
511     return nullptr;
512 }
513 
firstInfo()514 TickInfo* EquidistantTickIter::firstInfo()
515 {
516     if( m_pInfoTicks && gotoFirst() )
517         return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
518     return nullptr;
519 }
520 
getIntervalCount(sal_Int32 nDepth)521 sal_Int32 EquidistantTickIter::getIntervalCount( sal_Int32 nDepth )
522 {
523     if(nDepth>static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) || nDepth<0)
524         return 0;
525 
526     if(!nDepth)
527         return m_nTickCount;
528 
529     return m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
530 }
531 
isAtLastPartTick()532 bool EquidistantTickIter::isAtLastPartTick()
533 {
534     if(!m_nCurrentDepth)
535         return false;
536     sal_Int32 nIntervalCount = getIntervalCount( m_nCurrentDepth );
537     if(!nIntervalCount || nIntervalCount == 1)
538         return true;
539     if( m_pbIntervalFinished[m_nCurrentDepth] )
540         return false;
541     sal_Int32 nPos = m_pnPositions[m_nCurrentDepth]+1;
542     if(m_pnPreParentCount[m_nCurrentDepth])
543         nPos += nIntervalCount-1 - m_pnPreParentCount[m_nCurrentDepth];
544     bool bRet = nPos && nPos % (nIntervalCount-1) == 0;
545     if(!nPos && !m_pnPreParentCount[m_nCurrentDepth]
546              && m_pnPositions[m_nCurrentDepth-1]==-1 )
547          bRet = true;
548     return bRet;
549 }
550 
gotoFirst()551 bool EquidistantTickIter::gotoFirst()
552 {
553     if( m_nMaxDepth<0 )
554         return false;
555     if( !m_nTickCount )
556         return false;
557 
558     for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
559         m_pnPositions[nDepth] = -1;
560 
561     m_nCurrentPos   = 0;
562     m_nCurrentDepth = getStartDepth();
563     m_pnPositions[m_nCurrentDepth] = 0;
564     return true;
565 }
566 
gotoNext()567 bool EquidistantTickIter::gotoNext()
568 {
569     if( m_nCurrentPos < 0 )
570         return false;
571     m_nCurrentPos++;
572 
573     if( m_nCurrentPos >= m_nTickCount )
574         return false;
575 
576     if( m_nCurrentDepth==m_nMaxDepth && isAtLastPartTick() )
577     {
578         do
579         {
580             m_pbIntervalFinished[m_nCurrentDepth] = true;
581             m_nCurrentDepth--;
582         }
583         while( m_nCurrentDepth && isAtLastPartTick() );
584     }
585     else if( m_nCurrentDepth<m_nMaxDepth )
586     {
587         do
588         {
589             m_nCurrentDepth++;
590         }
591         while( m_nCurrentDepth<m_nMaxDepth );
592     }
593     m_pbIntervalFinished[m_nCurrentDepth] = false;
594     m_pnPositions[m_nCurrentDepth] = m_pnPositions[m_nCurrentDepth]+1;
595     return true;
596 }
597 
nextValue()598 double* EquidistantTickIter::nextValue()
599 {
600     if( gotoNext() )
601     {
602         m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]);
603         return &m_fCurrentValue;
604     }
605     return nullptr;
606 }
607 
nextInfo()608 TickInfo* EquidistantTickIter::nextInfo()
609 {
610     if( m_pInfoTicks && gotoNext() &&
611         static_cast< sal_Int32 >(
612             (*m_pInfoTicks)[m_nCurrentDepth].size()) > m_pnPositions[m_nCurrentDepth] )
613     {
614         return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
615     }
616     return nullptr;
617 }
618 
619 } //namespace chart
620 
621 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
622