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