1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
30 #include "Tickmarks_Equidistant.hxx"
31 #include "ViewDefines.hxx"
32 #include <rtl/math.hxx>
33 #include <tools/debug.hxx>
34 #include <memory>
35 
36 //.............................................................................
37 namespace chart
38 {
39 //.............................................................................
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::chart2;
42 using namespace ::rtl::math;
43 using ::basegfx::B2DVector;
44 
45 //static
46 double EquidistantTickFactory::getMinimumAtIncrement( double fMin, const ExplicitIncrementData& rIncrement )
47 {
48     //the returned value will be <= fMin and on a Major Tick given by rIncrement
49     if(rIncrement.Distance<=0.0)
50         return fMin;
51 
52     double fRet = rIncrement.BaseValue +
53         floor( approxSub( fMin, rIncrement.BaseValue )
54                     / rIncrement.Distance)
55             *rIncrement.Distance;
56 
57     if( fRet > fMin )
58     {
59         if( !approxEqual(fRet, fMin) )
60             fRet -= rIncrement.Distance;
61     }
62     return fRet;
63 }
64 //static
65 double EquidistantTickFactory::getMaximumAtIncrement( double fMax, const ExplicitIncrementData& rIncrement )
66 {
67     //the returned value will be >= fMax and on a Major Tick given by rIncrement
68     if(rIncrement.Distance<=0.0)
69         return fMax;
70 
71     double fRet = rIncrement.BaseValue +
72         floor( approxSub( fMax, rIncrement.BaseValue )
73                     / rIncrement.Distance)
74             *rIncrement.Distance;
75 
76     if( fRet < fMax )
77     {
78         if( !approxEqual(fRet, fMax) )
79             fRet += rIncrement.Distance;
80     }
81     return fRet;
82 }
83 
84 EquidistantTickFactory::EquidistantTickFactory(
85           const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement )
86             : m_rScale( rScale )
87             , m_rIncrement( rIncrement )
88             , m_xInverseScaling(NULL)
89             , m_pfCurrentValues(NULL)
90 {
91     //@todo: make sure that the scale is valid for the scaling
92 
93     m_pfCurrentValues = new double[getTickDepth()];
94 
95     if( m_rScale.Scaling.is() )
96     {
97         m_xInverseScaling = m_rScale.Scaling->getInverseScaling();
98         DBG_ASSERT( m_xInverseScaling.is(), "each Scaling needs to return a inverse Scaling" );
99     }
100 
101     double fMin = m_fScaledVisibleMin = m_rScale.Minimum;
102     if( m_xInverseScaling.is() )
103     {
104         m_fScaledVisibleMin = m_rScale.Scaling->doScaling(m_fScaledVisibleMin);
105         if(m_rIncrement.PostEquidistant )
106             fMin = m_fScaledVisibleMin;
107     }
108 
109     double fMax = m_fScaledVisibleMax = m_rScale.Maximum;
110     if( m_xInverseScaling.is() )
111     {
112         m_fScaledVisibleMax = m_rScale.Scaling->doScaling(m_fScaledVisibleMax);
113         if(m_rIncrement.PostEquidistant )
114             fMax = m_fScaledVisibleMax;
115     }
116 
117     //--
118     m_fOuterMajorTickBorderMin = EquidistantTickFactory::getMinimumAtIncrement( fMin, m_rIncrement );
119     m_fOuterMajorTickBorderMax = EquidistantTickFactory::getMaximumAtIncrement( fMax, m_rIncrement );
120     //--
121 
122     m_fOuterMajorTickBorderMin_Scaled = m_fOuterMajorTickBorderMin;
123     m_fOuterMajorTickBorderMax_Scaled = m_fOuterMajorTickBorderMax;
124     if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() )
125     {
126         m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin);
127         m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax);
128 
129         //check validity of new range: m_fOuterMajorTickBorderMin <-> m_fOuterMajorTickBorderMax
130         //it is assumed here, that the original range in the given Scale is valid
131         if( !rtl::math::isFinite(m_fOuterMajorTickBorderMin_Scaled) )
132         {
133             m_fOuterMajorTickBorderMin += m_rIncrement.Distance;
134             m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin);
135         }
136         if( !rtl::math::isFinite(m_fOuterMajorTickBorderMax_Scaled) )
137         {
138             m_fOuterMajorTickBorderMax -= m_rIncrement.Distance;
139             m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax);
140         }
141     }
142 }
143 
144 EquidistantTickFactory::~EquidistantTickFactory()
145 {
146     delete[] m_pfCurrentValues;
147 }
148 
149 sal_Int32 EquidistantTickFactory::getTickDepth() const
150 {
151     return static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) + 1;
152 }
153 
154 void EquidistantTickFactory::addSubTicks( sal_Int32 nDepth, uno::Sequence< uno::Sequence< double > >& rParentTicks ) const
155 {
156     EquidistantTickIter aIter( rParentTicks, m_rIncrement, 0, nDepth-1 );
157     double* pfNextParentTick = aIter.firstValue();
158     if(!pfNextParentTick)
159         return;
160     double fLastParentTick = *pfNextParentTick;
161     pfNextParentTick = aIter.nextValue();
162     if(!pfNextParentTick)
163         return;
164 
165     sal_Int32 nMaxSubTickCount = this->getMaxTickCount( nDepth );
166     if(!nMaxSubTickCount)
167         return;
168 
169     uno::Sequence< double > aSubTicks(nMaxSubTickCount);
170     sal_Int32 nRealSubTickCount = 0;
171     sal_Int32 nIntervalCount = m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
172 
173     double* pValue = NULL;
174     for(; pfNextParentTick; fLastParentTick=*pfNextParentTick, pfNextParentTick = aIter.nextValue())
175     {
176         for( sal_Int32 nPartTick = 1; nPartTick<nIntervalCount; nPartTick++ )
177         {
178             pValue = this->getMinorTick( nPartTick, nDepth
179                         , fLastParentTick, *pfNextParentTick );
180             if(!pValue)
181                 continue;
182 
183             aSubTicks[nRealSubTickCount] = *pValue;
184             nRealSubTickCount++;
185         }
186     }
187 
188     aSubTicks.realloc(nRealSubTickCount);
189     rParentTicks[nDepth] = aSubTicks;
190     if(static_cast<sal_Int32>(m_rIncrement.SubIncrements.size())>nDepth)
191         addSubTicks( nDepth+1, rParentTicks );
192 }
193 
194 
195 sal_Int32 EquidistantTickFactory::getMaxTickCount( sal_Int32 nDepth ) const
196 {
197     //return the maximum amount of ticks
198     //possibly open intervals at the two ends of the region are handled as if they were completely visible
199     //(this is necessary for calculating the sub ticks at the borders correctly)
200 
201     if( nDepth >= getTickDepth() )
202         return 0;
203     if( m_fOuterMajorTickBorderMax < m_fOuterMajorTickBorderMin )
204         return 0;
205     if( m_rIncrement.Distance<=0.0)
206         return 0;
207 
208     double fSub;
209     if(m_rIncrement.PostEquidistant  )
210         fSub = approxSub( m_fScaledVisibleMax, m_fScaledVisibleMin );
211     else
212         fSub = approxSub( m_rScale.Maximum, m_rScale.Minimum );
213 
214     if (!isFinite(fSub))
215         return 0;
216 
217     sal_Int32 nIntervalCount = static_cast<sal_Int32>( fSub / m_rIncrement.Distance );
218 
219     nIntervalCount+=3;
220     for(sal_Int32 nN=0; nN<nDepth-1; nN++)
221     {
222         if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
223             nIntervalCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
224     }
225 
226     sal_Int32 nTickCount = nIntervalCount;
227     if(nDepth>0 && m_rIncrement.SubIncrements[nDepth-1].IntervalCount>1)
228         nTickCount = nIntervalCount * (m_rIncrement.SubIncrements[nDepth-1].IntervalCount-1);
229 
230     return nTickCount;
231 }
232 
233 double* EquidistantTickFactory::getMajorTick( sal_Int32 nTick ) const
234 {
235     m_pfCurrentValues[0] = m_fOuterMajorTickBorderMin + nTick*m_rIncrement.Distance;
236 
237     if(m_pfCurrentValues[0]>m_fOuterMajorTickBorderMax)
238     {
239         if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMax) )
240             return NULL;
241     }
242     if(m_pfCurrentValues[0]<m_fOuterMajorTickBorderMin)
243     {
244         if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMin) )
245             return NULL;
246     }
247 
248     //return always the value after scaling
249     if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() )
250         m_pfCurrentValues[0] = m_rScale.Scaling->doScaling( m_pfCurrentValues[0] );
251 
252     return &m_pfCurrentValues[0];
253 }
254 
255 double* EquidistantTickFactory::getMinorTick( sal_Int32 nTick, sal_Int32 nDepth
256                             , double fStartParentTick, double fNextParentTick ) const
257 {
258     //check validity of arguments
259     {
260         //DBG_ASSERT( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick");
261         if(fStartParentTick >= fNextParentTick)
262             return NULL;
263         if(nDepth>static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) || nDepth<=0)
264             return NULL;
265 
266         //subticks are only calculated if they are laying between parent ticks:
267         if(nTick<=0)
268             return NULL;
269         if(nTick>=m_rIncrement.SubIncrements[nDepth-1].IntervalCount)
270             return NULL;
271     }
272 
273     bool    bPostEquidistant = m_rIncrement.SubIncrements[nDepth-1].PostEquidistant;
274 
275     double fAdaptedStartParent = fStartParentTick;
276     double fAdaptedNextParent  = fNextParentTick;
277 
278     if( !bPostEquidistant && m_xInverseScaling.is() )
279     {
280         fAdaptedStartParent = m_xInverseScaling->doScaling(fStartParentTick);
281         fAdaptedNextParent  = m_xInverseScaling->doScaling(fNextParentTick);
282     }
283 
284     double fDistance = (fAdaptedNextParent - fAdaptedStartParent)/m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
285 
286     m_pfCurrentValues[nDepth] = fAdaptedStartParent + nTick*fDistance;
287 
288     //return always the value after scaling
289     if(!bPostEquidistant && m_xInverseScaling.is() )
290         m_pfCurrentValues[nDepth] = m_rScale.Scaling->doScaling( m_pfCurrentValues[nDepth] );
291 
292     if( !isWithinOuterBorder( m_pfCurrentValues[nDepth] ) )
293         return NULL;
294 
295     return &m_pfCurrentValues[nDepth];
296 }
297 
298 bool EquidistantTickFactory::isWithinOuterBorder( double fScaledValue ) const
299 {
300     if(fScaledValue>m_fOuterMajorTickBorderMax_Scaled)
301         return false;
302     if(fScaledValue<m_fOuterMajorTickBorderMin_Scaled)
303         return false;
304 
305     return true;
306 }
307 
308 bool EquidistantTickFactory::isVisible( double fScaledValue ) const
309 {
310     if(fScaledValue>m_fScaledVisibleMax)
311     {
312         if( !approxEqual(fScaledValue,m_fScaledVisibleMax) )
313             return false;
314     }
315     if(fScaledValue<m_fScaledVisibleMin)
316     {
317         if( !approxEqual(fScaledValue,m_fScaledVisibleMin) )
318             return false;
319     }
320     return true;
321 }
322 
323 void EquidistantTickFactory::getAllTicks( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
324 {
325     uno::Sequence< uno::Sequence< double > > aAllTicks;
326 
327     //create point sequences for each tick depth
328     sal_Int32 nDepthCount = this->getTickDepth();
329     sal_Int32 nMaxMajorTickCount = this->getMaxTickCount( 0 );
330 
331     aAllTicks.realloc(nDepthCount);
332     aAllTicks[0].realloc(nMaxMajorTickCount);
333 
334     sal_Int32 nRealMajorTickCount = 0;
335     double* pValue = NULL;
336     for( sal_Int32 nMajorTick=0; nMajorTick<nMaxMajorTickCount; nMajorTick++ )
337     {
338         pValue = this->getMajorTick( nMajorTick );
339         if(!pValue)
340             continue;
341         aAllTicks[0][nRealMajorTickCount] = *pValue;
342         nRealMajorTickCount++;
343     }
344     if(!nRealMajorTickCount)
345         return;
346     aAllTicks[0].realloc(nRealMajorTickCount);
347 
348     if(nDepthCount>0)
349         this->addSubTicks( 1, aAllTicks );
350 
351     //so far we have added all ticks between the outer major tick marks
352     //this was necessary to create sub ticks correctly
353     //now we reduce all ticks to the visible ones that lie between the real borders
354     sal_Int32 nDepth = 0;
355     sal_Int32 nTick = 0;
356     for( nDepth = 0; nDepth < nDepthCount; nDepth++)
357     {
358         sal_Int32 nInvisibleAtLowerBorder = 0;
359         sal_Int32 nInvisibleAtUpperBorder = 0;
360         //we need only to check all ticks within the first major interval at each border
361         sal_Int32 nCheckCount = 1;
362         for(sal_Int32 nN=0; nN<nDepth; nN++)
363         {
364             if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
365                 nCheckCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
366         }
367         uno::Sequence< double >& rTicks = aAllTicks[nDepth];
368         sal_Int32 nCount = rTicks.getLength();
369         //check lower border
370         for( nTick=0; nTick<nCheckCount && nTick<nCount; nTick++)
371         {
372             if( !isVisible( rTicks[nTick] ) )
373                 nInvisibleAtLowerBorder++;
374         }
375         //check upper border
376         for( nTick=nCount-1; nTick>nCount-1-nCheckCount && nTick>=0; nTick--)
377         {
378             if( !isVisible( rTicks[nTick] ) )
379                 nInvisibleAtUpperBorder++;
380         }
381         //resize sequence
382         if( !nInvisibleAtLowerBorder && !nInvisibleAtUpperBorder)
383             continue;
384         if( !nInvisibleAtLowerBorder )
385             rTicks.realloc(nCount-nInvisibleAtUpperBorder);
386         else
387         {
388             sal_Int32 nNewCount = nCount-nInvisibleAtUpperBorder-nInvisibleAtLowerBorder;
389             if(nNewCount<0)
390                 nNewCount=0;
391 
392             uno::Sequence< double > aOldTicks(rTicks);
393             rTicks.realloc(nNewCount);
394             for(nTick = 0; nTick<nNewCount; nTick++)
395                 rTicks[nTick] = aOldTicks[nInvisibleAtLowerBorder+nTick];
396         }
397     }
398 
399     //fill return value
400     rAllTickInfos.resize(aAllTicks.getLength());
401     for( nDepth=0 ;nDepth<aAllTicks.getLength(); nDepth++ )
402     {
403         sal_Int32 nCount = aAllTicks[nDepth].getLength();
404 
405         ::std::vector< TickInfo >& rTickInfoVector = rAllTickInfos[nDepth];
406         rTickInfoVector.clear();
407         rTickInfoVector.reserve( nCount );
408         for(sal_Int32 nN = 0; nN<nCount; nN++)
409         {
410             TickInfo aTickInfo(m_xInverseScaling);
411             aTickInfo.fScaledTickValue = aAllTicks[nDepth][nN];
412             rTickInfoVector.push_back(aTickInfo);
413         }
414     }
415 }
416 
417 void EquidistantTickFactory::getAllTicksShifted( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
418 {
419     ExplicitIncrementData aShiftedIncrement( m_rIncrement );
420     aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0;
421     EquidistantTickFactory( m_rScale, aShiftedIncrement ).getAllTicks(rAllTickInfos);
422 }
423 
424 //-----------------------------------------------------------------------------
425 //-----------------------------------------------------------------------------
426 //-----------------------------------------------------------------------------
427 
428 EquidistantTickIter::EquidistantTickIter( const uno::Sequence< uno::Sequence< double > >& rTicks
429                    , const ExplicitIncrementData& rIncrement
430                    , sal_Int32 nMinDepth, sal_Int32 nMaxDepth )
431                 : m_pSimpleTicks(&rTicks)
432                 , m_pInfoTicks(0)
433                 , m_rIncrement(rIncrement)
434                 , m_nMinDepth(0), m_nMaxDepth(0)
435                 , m_nTickCount(0), m_pnPositions(NULL)
436                 , m_pnPreParentCount(NULL), m_pbIntervalFinished(NULL)
437                 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
438 {
439     initIter( nMinDepth, nMaxDepth );
440 }
441 
442 EquidistantTickIter::EquidistantTickIter( ::std::vector< ::std::vector< TickInfo > >& rTicks
443                    , const ExplicitIncrementData& rIncrement
444                    , sal_Int32 nMinDepth, sal_Int32 nMaxDepth )
445                 : m_pSimpleTicks(NULL)
446                 , m_pInfoTicks(&rTicks)
447                 , m_rIncrement(rIncrement)
448                 , m_nMinDepth(0), m_nMaxDepth(0)
449                 , m_nTickCount(0), m_pnPositions(NULL)
450                 , m_pnPreParentCount(NULL), m_pbIntervalFinished(NULL)
451                 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
452 {
453     initIter( nMinDepth, nMaxDepth );
454 }
455 
456 void EquidistantTickIter::initIter( sal_Int32 /*nMinDepth*/, sal_Int32 nMaxDepth )
457 {
458     m_nMaxDepth = nMaxDepth;
459     if(nMaxDepth<0 || m_nMaxDepth>getMaxDepth())
460         m_nMaxDepth=getMaxDepth();
461 
462     sal_Int32 nDepth = 0;
463     for( nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
464         m_nTickCount += getTickCount(nDepth);
465 
466     if(!m_nTickCount)
467         return;
468 
469     m_pnPositions      = new sal_Int32[m_nMaxDepth+1];
470 
471     m_pnPreParentCount = new sal_Int32[m_nMaxDepth+1];
472     m_pbIntervalFinished = new bool[m_nMaxDepth+1];
473     m_pnPreParentCount[0] = 0;
474     m_pbIntervalFinished[0] = false;
475     double fParentValue = getTickValue(0,0);
476     for( nDepth = 1; nDepth<=m_nMaxDepth ;nDepth++ )
477     {
478         m_pbIntervalFinished[nDepth] = false;
479 
480         sal_Int32 nPreParentCount = 0;
481         sal_Int32 nCount = getTickCount(nDepth);
482         for(sal_Int32 nN = 0; nN<nCount; nN++)
483         {
484             if(getTickValue(nDepth,nN) < fParentValue)
485                 nPreParentCount++;
486             else
487                 break;
488         }
489         m_pnPreParentCount[nDepth] = nPreParentCount;
490         if(nCount)
491         {
492             double fNextParentValue = getTickValue(nDepth,0);
493             if( fNextParentValue < fParentValue )
494                 fParentValue = fNextParentValue;
495         }
496     }
497 }
498 
499 EquidistantTickIter::~EquidistantTickIter()
500 {
501     delete[] m_pnPositions;
502     delete[] m_pnPreParentCount;
503     delete[] m_pbIntervalFinished;
504 }
505 
506 sal_Int32 EquidistantTickIter::getStartDepth() const
507 {
508     //find the depth of the first visible tickmark:
509     //it is the depth of the smallest value
510     sal_Int32 nReturnDepth=0;
511     double fMinValue = DBL_MAX;
512     for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
513     {
514         sal_Int32 nCount = getTickCount(nDepth);
515         if( !nCount )
516             continue;
517         double fThisValue = getTickValue(nDepth,0);
518         if(fThisValue<fMinValue)
519         {
520             nReturnDepth = nDepth;
521             fMinValue = fThisValue;
522         }
523     }
524     return nReturnDepth;
525 }
526 
527 double* EquidistantTickIter::firstValue()
528 {
529     if( gotoFirst() )
530     {
531         m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]);
532         return &m_fCurrentValue;
533     }
534     return NULL;
535 }
536 
537 TickInfo* EquidistantTickIter::firstInfo()
538 {
539     if( m_pInfoTicks && gotoFirst() )
540         return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
541     return NULL;
542 }
543 
544 sal_Int32 EquidistantTickIter::getIntervalCount( sal_Int32 nDepth )
545 {
546     if(nDepth>static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) || nDepth<0)
547         return 0;
548 
549     if(!nDepth)
550         return m_nTickCount;
551 
552     return m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
553 }
554 
555 bool EquidistantTickIter::isAtLastPartTick()
556 {
557     if(!m_nCurrentDepth)
558         return false;
559     sal_Int32 nIntervalCount = getIntervalCount( m_nCurrentDepth );
560     if(!nIntervalCount || nIntervalCount == 1)
561         return true;
562     if( m_pbIntervalFinished[m_nCurrentDepth] )
563         return false;
564     sal_Int32 nPos = m_pnPositions[m_nCurrentDepth]+1;
565     if(m_pnPreParentCount[m_nCurrentDepth])
566         nPos += nIntervalCount-1 - m_pnPreParentCount[m_nCurrentDepth];
567     bool bRet = nPos && nPos % (nIntervalCount-1) == 0;
568     if(!nPos && !m_pnPreParentCount[m_nCurrentDepth]
569              && m_pnPositions[m_nCurrentDepth-1]==-1 )
570          bRet = true;
571     return bRet;
572 }
573 
574 bool EquidistantTickIter::gotoFirst()
575 {
576     if( m_nMaxDepth<0 )
577         return false;
578     if( !m_nTickCount )
579         return false;
580 
581     for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
582         m_pnPositions[nDepth] = -1;
583 
584     m_nCurrentPos   = 0;
585     m_nCurrentDepth = getStartDepth();
586     m_pnPositions[m_nCurrentDepth] = 0;
587     return true;
588 }
589 
590 bool EquidistantTickIter::gotoNext()
591 {
592     if( m_nCurrentPos < 0 )
593         return false;
594     m_nCurrentPos++;
595 
596     if( m_nCurrentPos >= m_nTickCount )
597         return false;
598 
599     if( m_nCurrentDepth==m_nMaxDepth && isAtLastPartTick() )
600     {
601         do
602         {
603             m_pbIntervalFinished[m_nCurrentDepth] = true;
604             m_nCurrentDepth--;
605         }
606         while( m_nCurrentDepth && isAtLastPartTick() );
607     }
608     else if( m_nCurrentDepth<m_nMaxDepth )
609     {
610         do
611         {
612             m_nCurrentDepth++;
613         }
614         while( m_nCurrentDepth<m_nMaxDepth );
615     }
616     m_pbIntervalFinished[m_nCurrentDepth] = false;
617     m_pnPositions[m_nCurrentDepth] = m_pnPositions[m_nCurrentDepth]+1;
618     return true;
619 }
620 
621 bool EquidistantTickIter::gotoIndex( sal_Int32 nTickIndex )
622 {
623     if( nTickIndex < 0 )
624         return false;
625     if( nTickIndex >= m_nTickCount )
626         return false;
627 
628     if( nTickIndex < m_nCurrentPos )
629         if( !gotoFirst() )
630             return false;
631 
632     while( nTickIndex > m_nCurrentPos )
633         if( !gotoNext() )
634             return false;
635 
636     return true;
637 }
638 
639 sal_Int32 EquidistantTickIter::getCurrentIndex() const
640 {
641     return m_nCurrentPos;
642 }
643 sal_Int32 EquidistantTickIter::getMaxIndex() const
644 {
645     return m_nTickCount-1;
646 }
647 
648 double* EquidistantTickIter::nextValue()
649 {
650     if( gotoNext() )
651     {
652         m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]);
653         return &m_fCurrentValue;
654     }
655     return NULL;
656 }
657 
658 TickInfo* EquidistantTickIter::nextInfo()
659 {
660     if( m_pInfoTicks && gotoNext() &&
661         static_cast< sal_Int32 >(
662             (*m_pInfoTicks)[m_nCurrentDepth].size()) > m_pnPositions[m_nCurrentDepth] )
663     {
664         return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
665     }
666     return NULL;
667 }
668 
669 //.............................................................................
670 } //namespace chart
671 //.............................................................................
672