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 "TickmarkHelper.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 TickInfo::TickInfo()
46 : fScaledTickValue( 0.0 )
47 , fUnscaledTickValue( 0.0 )
48 , aTickScreenPosition(0.0,0.0)
49 , bPaintIt( true )
50 , xTextShape( NULL )
51 , nFactorForLimitedTextWidth(1)
52 {
53 }
54 
55 void TickInfo::updateUnscaledValue( const uno::Reference< XScaling >& xInverseScaling )
56 {
57     if( xInverseScaling.is() )
58         this->fUnscaledTickValue = xInverseScaling->doScaling( this->fScaledTickValue );
59     else
60         this->fUnscaledTickValue = this->fScaledTickValue;
61 }
62 
63 sal_Int32 TickInfo::getScreenDistanceBetweenTicks( const TickInfo& rOherTickInfo ) const
64 {
65     //return the positive distance between the two first tickmarks in screen values
66 
67     B2DVector aDistance = rOherTickInfo.aTickScreenPosition - aTickScreenPosition;
68     sal_Int32 nRet = static_cast<sal_Int32>(aDistance.getLength());
69     if(nRet<0)
70         nRet *= -1;
71     return nRet;
72 }
73 
74 PureTickIter::PureTickIter( ::std::vector< TickInfo >& rTickInfoVector )
75             : m_rTickVector(rTickInfoVector)
76             , m_aTickIter(m_rTickVector.begin())
77 {
78 }
79 PureTickIter::~PureTickIter()
80 {
81 }
82 TickInfo* PureTickIter::firstInfo()
83 {
84     m_aTickIter = m_rTickVector.begin();
85     if(m_aTickIter!=m_rTickVector.end())
86         return &*m_aTickIter;
87     return 0;
88 }
89 TickInfo* PureTickIter::nextInfo()
90 {
91     m_aTickIter++;
92     if(m_aTickIter!=m_rTickVector.end())
93         return &*m_aTickIter;
94     return 0;
95 }
96 
97 EquidistantTickIter::EquidistantTickIter( const uno::Sequence< uno::Sequence< double > >& rTicks
98                    , const ExplicitIncrementData& rIncrement
99                    , sal_Int32 nMinDepth, sal_Int32 nMaxDepth )
100                 : m_pSimpleTicks(&rTicks)
101                 , m_pInfoTicks(0)
102                 , m_rIncrement(rIncrement)
103                 , m_nMinDepth(0), m_nMaxDepth(0)
104                 , m_nTickCount(0), m_pnPositions(NULL)
105                 , m_pnPreParentCount(NULL), m_pbIntervalFinished(NULL)
106                 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
107 {
108     initIter( nMinDepth, nMaxDepth );
109 }
110 
111 EquidistantTickIter::EquidistantTickIter( ::std::vector< ::std::vector< TickInfo > >& rTicks
112                    , const ExplicitIncrementData& rIncrement
113                    , sal_Int32 nMinDepth, sal_Int32 nMaxDepth )
114                 : m_pSimpleTicks(NULL)
115                 , m_pInfoTicks(&rTicks)
116                 , m_rIncrement(rIncrement)
117                 , m_nMinDepth(0), m_nMaxDepth(0)
118                 , m_nTickCount(0), m_pnPositions(NULL)
119                 , m_pnPreParentCount(NULL), m_pbIntervalFinished(NULL)
120                 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
121 {
122     initIter( nMinDepth, nMaxDepth );
123 }
124 
125 void EquidistantTickIter::initIter( sal_Int32 /*nMinDepth*/, sal_Int32 nMaxDepth )
126 {
127     m_nMaxDepth = nMaxDepth;
128     if(nMaxDepth<0 || m_nMaxDepth>getMaxDepth())
129         m_nMaxDepth=getMaxDepth();
130 
131     sal_Int32 nDepth = 0;
132     for( nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
133         m_nTickCount += getTickCount(nDepth);
134 
135     if(!m_nTickCount)
136         return;
137 
138     m_pnPositions      = new sal_Int32[m_nMaxDepth+1];
139 
140     m_pnPreParentCount = new sal_Int32[m_nMaxDepth+1];
141     m_pbIntervalFinished = new bool[m_nMaxDepth+1];
142     m_pnPreParentCount[0] = 0;
143     m_pbIntervalFinished[0] = false;
144     double fParentValue = getTickValue(0,0);
145     for( nDepth = 1; nDepth<=m_nMaxDepth ;nDepth++ )
146     {
147         m_pbIntervalFinished[nDepth] = false;
148 
149         sal_Int32 nPreParentCount = 0;
150         sal_Int32 nCount = getTickCount(nDepth);
151         for(sal_Int32 nN = 0; nN<nCount; nN++)
152         {
153             if(getTickValue(nDepth,nN) < fParentValue)
154                 nPreParentCount++;
155             else
156                 break;
157         }
158         m_pnPreParentCount[nDepth] = nPreParentCount;
159         if(nCount)
160         {
161             double fNextParentValue = getTickValue(nDepth,0);
162             if( fNextParentValue < fParentValue )
163                 fParentValue = fNextParentValue;
164         }
165     }
166 }
167 
168 EquidistantTickIter::~EquidistantTickIter()
169 {
170     delete[] m_pnPositions;
171     delete[] m_pnPreParentCount;
172     delete[] m_pbIntervalFinished;
173 }
174 
175 sal_Int32 EquidistantTickIter::getStartDepth() const
176 {
177     //find the depth of the first visible tickmark:
178     //it is the depth of the smallest value
179     sal_Int32 nReturnDepth=0;
180     double fMinValue = DBL_MAX;
181     for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
182     {
183         sal_Int32 nCount = getTickCount(nDepth);
184         if( !nCount )
185             continue;
186         double fThisValue = getTickValue(nDepth,0);
187         if(fThisValue<fMinValue)
188         {
189             nReturnDepth = nDepth;
190             fMinValue = fThisValue;
191         }
192     }
193     return nReturnDepth;
194 }
195 
196 double* EquidistantTickIter::firstValue()
197 {
198     if( gotoFirst() )
199     {
200         m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]);
201         return &m_fCurrentValue;
202     }
203     return NULL;
204 }
205 
206 TickInfo* EquidistantTickIter::firstInfo()
207 {
208     if( m_pInfoTicks && gotoFirst() )
209         return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
210     return NULL;
211 }
212 
213 sal_Int32 EquidistantTickIter::getIntervalCount( sal_Int32 nDepth )
214 {
215     if(nDepth>m_rIncrement.SubIncrements.getLength() || nDepth<0)
216         return 0;
217 
218     if(!nDepth)
219         return m_nTickCount;
220 
221     return m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
222 }
223 
224 bool EquidistantTickIter::isAtLastPartTick()
225 {
226     if(!m_nCurrentDepth)
227         return false;
228     sal_Int32 nIntervalCount = getIntervalCount( m_nCurrentDepth );
229     if(!nIntervalCount || nIntervalCount == 1)
230         return true;
231     if( m_pbIntervalFinished[m_nCurrentDepth] )
232         return false;
233     sal_Int32 nPos = m_pnPositions[m_nCurrentDepth]+1;
234     if(m_pnPreParentCount[m_nCurrentDepth])
235         nPos += nIntervalCount-1 - m_pnPreParentCount[m_nCurrentDepth];
236     bool bRet = nPos && nPos % (nIntervalCount-1) == 0;
237     if(!nPos && !m_pnPreParentCount[m_nCurrentDepth]
238              && m_pnPositions[m_nCurrentDepth-1]==-1 )
239          bRet = true;
240     return bRet;
241 }
242 
243 bool EquidistantTickIter::gotoFirst()
244 {
245     if( m_nMaxDepth<0 )
246         return false;
247     if( !m_nTickCount )
248         return false;
249 
250     for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
251         m_pnPositions[nDepth] = -1;
252 
253     m_nCurrentPos   = 0;
254     m_nCurrentDepth = getStartDepth();
255     m_pnPositions[m_nCurrentDepth] = 0;
256     return true;
257 }
258 
259 bool EquidistantTickIter::gotoNext()
260 {
261     if( m_nCurrentPos < 0 )
262         return false;
263     m_nCurrentPos++;
264 
265     if( m_nCurrentPos >= m_nTickCount )
266         return false;
267 
268     if( m_nCurrentDepth==m_nMaxDepth && isAtLastPartTick() )
269     {
270         do
271         {
272             m_pbIntervalFinished[m_nCurrentDepth] = true;
273             m_nCurrentDepth--;
274         }
275         while( m_nCurrentDepth && isAtLastPartTick() );
276     }
277     else if( m_nCurrentDepth<m_nMaxDepth )
278     {
279         do
280         {
281             m_nCurrentDepth++;
282         }
283         while( m_nCurrentDepth<m_nMaxDepth );
284     }
285     m_pbIntervalFinished[m_nCurrentDepth] = false;
286     m_pnPositions[m_nCurrentDepth] = m_pnPositions[m_nCurrentDepth]+1;
287     return true;
288 }
289 
290 bool EquidistantTickIter::gotoIndex( sal_Int32 nTickIndex )
291 {
292     if( nTickIndex < 0 )
293         return false;
294     if( nTickIndex >= m_nTickCount )
295         return false;
296 
297     if( nTickIndex < m_nCurrentPos )
298         if( !gotoFirst() )
299             return false;
300 
301     while( nTickIndex > m_nCurrentPos )
302         if( !gotoNext() )
303             return false;
304 
305     return true;
306 }
307 
308 sal_Int32 EquidistantTickIter::getCurrentIndex() const
309 {
310     return m_nCurrentPos;
311 }
312 sal_Int32 EquidistantTickIter::getMaxIndex() const
313 {
314     return m_nTickCount-1;
315 }
316 
317 double* EquidistantTickIter::nextValue()
318 {
319     if( gotoNext() )
320     {
321         m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]);
322         return &m_fCurrentValue;
323     }
324     return NULL;
325 }
326 
327 TickInfo* EquidistantTickIter::nextInfo()
328 {
329     if( m_pInfoTicks && gotoNext() &&
330         static_cast< sal_Int32 >(
331             (*m_pInfoTicks)[m_nCurrentDepth].size()) > m_pnPositions[m_nCurrentDepth] )
332     {
333         return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
334     }
335     return NULL;
336 }
337 
338 //-----------------------------------------------------------------------------
339 //-----------------------------------------------------------------------------
340 //-----------------------------------------------------------------------------
341 
342 double TickmarkHelper::getMinimumAtIncrement( double fMin, const ExplicitIncrementData& rIncrement )
343 {
344     //the returned value will be <= fMin and on a Major Tick given by rIncrement
345     if(rIncrement.Distance<=0.0)
346         return fMin;
347 
348     double fRet = rIncrement.BaseValue +
349         floor( approxSub( fMin, rIncrement.BaseValue )
350                     / rIncrement.Distance)
351             *rIncrement.Distance;
352 
353     if( fRet > fMin )
354     {
355         if( !approxEqual(fRet, fMin) )
356             fRet -= rIncrement.Distance;
357     }
358     return fRet;
359 }
360 double TickmarkHelper::getMaximumAtIncrement( double fMax, const ExplicitIncrementData& rIncrement )
361 {
362     //the returned value will be >= fMax and on a Major Tick given by rIncrement
363     if(rIncrement.Distance<=0.0)
364         return fMax;
365 
366     double fRet = rIncrement.BaseValue +
367         floor( approxSub( fMax, rIncrement.BaseValue )
368                     / rIncrement.Distance)
369             *rIncrement.Distance;
370 
371     if( fRet < fMax )
372     {
373         if( !approxEqual(fRet, fMax) )
374             fRet += rIncrement.Distance;
375     }
376     return fRet;
377 }
378 
379 //-----------------------------------------------------------------------------
380 //-----------------------------------------------------------------------------
381 //-----------------------------------------------------------------------------
382 
383 TickmarkHelper::TickmarkHelper(
384           const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement )
385             : m_rScale( rScale )
386             , m_rIncrement( rIncrement )
387             , m_xInverseScaling(NULL)
388             , m_pfCurrentValues(NULL)
389 {
390     //@todo: make sure that the scale is valid for the scaling
391 
392     m_pfCurrentValues = new double[getTickDepth()];
393 
394     if( m_rScale.Scaling.is() )
395     {
396         m_xInverseScaling = m_rScale.Scaling->getInverseScaling();
397         DBG_ASSERT( m_xInverseScaling.is(), "each Scaling needs to return a inverse Scaling" );
398     }
399 
400     double fMin = m_fScaledVisibleMin = m_rScale.Minimum;
401     if( m_xInverseScaling.is() )
402     {
403         m_fScaledVisibleMin = m_rScale.Scaling->doScaling(m_fScaledVisibleMin);
404         if(m_rIncrement.PostEquidistant )
405             fMin = m_fScaledVisibleMin;
406     }
407 
408     double fMax = m_fScaledVisibleMax = m_rScale.Maximum;
409     if( m_xInverseScaling.is() )
410     {
411         m_fScaledVisibleMax = m_rScale.Scaling->doScaling(m_fScaledVisibleMax);
412         if(m_rIncrement.PostEquidistant )
413             fMax = m_fScaledVisibleMax;
414     }
415 
416     //--
417     m_fOuterMajorTickBorderMin = TickmarkHelper::getMinimumAtIncrement( fMin, m_rIncrement );
418     m_fOuterMajorTickBorderMax = TickmarkHelper::getMaximumAtIncrement( fMax, m_rIncrement );
419     //--
420 
421     m_fOuterMajorTickBorderMin_Scaled = m_fOuterMajorTickBorderMin;
422     m_fOuterMajorTickBorderMax_Scaled = m_fOuterMajorTickBorderMax;
423     if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() )
424     {
425         m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin);
426         m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax);
427 
428         //check validity of new range: m_fOuterMajorTickBorderMin <-> m_fOuterMajorTickBorderMax
429         //it is assumed here, that the original range in the given Scale is valid
430         if( !rtl::math::isFinite(m_fOuterMajorTickBorderMin_Scaled) )
431         {
432             m_fOuterMajorTickBorderMin += m_rIncrement.Distance;
433             m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin);
434         }
435         if( !rtl::math::isFinite(m_fOuterMajorTickBorderMax_Scaled) )
436         {
437             m_fOuterMajorTickBorderMax -= m_rIncrement.Distance;
438             m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax);
439         }
440     }
441 }
442 
443 TickmarkHelper* TickmarkHelper::createShiftedTickmarkHelper() const
444 {
445     ExplicitIncrementData aShiftedIncrement( m_rIncrement );
446     aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0;
447     return new TickmarkHelper( m_rScale, aShiftedIncrement );
448 }
449 
450 TickmarkHelper::~TickmarkHelper()
451 {
452     delete[] m_pfCurrentValues;
453 }
454 
455 sal_Int32 TickmarkHelper::getTickDepth() const
456 {
457     return m_rIncrement.SubIncrements.getLength() + 1;
458 }
459 
460 sal_Int32 TickmarkHelper::getMaxTickCount( sal_Int32 nDepth ) const
461 {
462     //return the maximum amount of ticks
463     //possibly open intervals at the two ends of the region are handled as if they were completely visible
464     //(this is necessary for calculating the sub ticks at the borders correctly)
465 
466     if( nDepth >= getTickDepth() )
467         return 0;
468     if( m_fOuterMajorTickBorderMax < m_fOuterMajorTickBorderMin )
469         return 0;
470     if( m_rIncrement.Distance<=0.0)
471         return 0;
472 
473     double fSub;
474     if(m_rIncrement.PostEquidistant  )
475         fSub = approxSub( m_fScaledVisibleMax, m_fScaledVisibleMin );
476     else
477         fSub = approxSub( m_rScale.Maximum, m_rScale.Minimum );
478 
479     if (!isFinite(fSub))
480         return 0;
481 
482     sal_Int32 nIntervalCount = static_cast<sal_Int32>( fSub / m_rIncrement.Distance );
483 
484     nIntervalCount+=3;
485     for(sal_Int32 nN=0; nN<nDepth-1; nN++)
486     {
487         if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
488             nIntervalCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
489     }
490 
491     sal_Int32 nTickCount = nIntervalCount;
492     if(nDepth>0 && m_rIncrement.SubIncrements[nDepth-1].IntervalCount>1)
493         nTickCount = nIntervalCount * (m_rIncrement.SubIncrements[nDepth-1].IntervalCount-1);
494 
495     return nTickCount;
496 }
497 
498 double* TickmarkHelper::getMajorTick( sal_Int32 nTick ) const
499 {
500     m_pfCurrentValues[0] = m_fOuterMajorTickBorderMin + nTick*m_rIncrement.Distance;
501 
502     if(m_pfCurrentValues[0]>m_fOuterMajorTickBorderMax)
503     {
504         if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMax) )
505             return NULL;
506     }
507     if(m_pfCurrentValues[0]<m_fOuterMajorTickBorderMin)
508     {
509         if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMin) )
510             return NULL;
511     }
512 
513     //return always the value after scaling
514     if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() )
515         m_pfCurrentValues[0] = m_rScale.Scaling->doScaling( m_pfCurrentValues[0] );
516 
517     return &m_pfCurrentValues[0];
518 }
519 
520 double* TickmarkHelper::getMinorTick( sal_Int32 nTick, sal_Int32 nDepth
521                             , double fStartParentTick, double fNextParentTick ) const
522 {
523     //check validity of arguments
524     {
525         //DBG_ASSERT( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick");
526         if(fStartParentTick >= fNextParentTick)
527             return NULL;
528         if(nDepth>m_rIncrement.SubIncrements.getLength() || nDepth<=0)
529             return NULL;
530 
531         //subticks are only calculated if they are laying between parent ticks:
532         if(nTick<=0)
533             return NULL;
534         if(nTick>=m_rIncrement.SubIncrements[nDepth-1].IntervalCount)
535             return NULL;
536     }
537 
538     bool    bPostEquidistant = m_rIncrement.SubIncrements[nDepth-1].PostEquidistant;
539 
540     double fAdaptedStartParent = fStartParentTick;
541     double fAdaptedNextParent  = fNextParentTick;
542 
543     if( !bPostEquidistant && m_xInverseScaling.is() )
544     {
545         fAdaptedStartParent = m_xInverseScaling->doScaling(fStartParentTick);
546         fAdaptedNextParent  = m_xInverseScaling->doScaling(fNextParentTick);
547     }
548 
549     double fDistance = (fAdaptedNextParent - fAdaptedStartParent)/m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
550 
551     m_pfCurrentValues[nDepth] = fAdaptedStartParent + nTick*fDistance;
552 
553     //return always the value after scaling
554     if(!bPostEquidistant && m_xInverseScaling.is() )
555         m_pfCurrentValues[nDepth] = m_rScale.Scaling->doScaling( m_pfCurrentValues[nDepth] );
556 
557     if( !isWithinOuterBorder( m_pfCurrentValues[nDepth] ) )
558         return NULL;
559 
560     return &m_pfCurrentValues[nDepth];
561 }
562 
563 bool TickmarkHelper::isWithinOuterBorder( double fScaledValue ) const
564 {
565     if(fScaledValue>m_fOuterMajorTickBorderMax_Scaled)
566         return false;
567     if(fScaledValue<m_fOuterMajorTickBorderMin_Scaled)
568         return false;
569 
570     return true;
571 }
572 
573 
574 bool TickmarkHelper::isVisible( double fScaledValue ) const
575 {
576     if(fScaledValue>m_fScaledVisibleMax)
577     {
578         if( !approxEqual(fScaledValue,m_fScaledVisibleMax) )
579             return false;
580     }
581     if(fScaledValue<m_fScaledVisibleMin)
582     {
583         if( !approxEqual(fScaledValue,m_fScaledVisibleMin) )
584             return false;
585     }
586     return true;
587 }
588 
589 void TickmarkHelper::getAllTicks( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
590 {
591     uno::Sequence< uno::Sequence< double > > aAllTicks;
592 
593     //create point sequences for each tick depth
594     sal_Int32 nDepthCount = this->getTickDepth();
595     sal_Int32 nMaxMajorTickCount = this->getMaxTickCount( 0 );
596 
597     aAllTicks.realloc(nDepthCount);
598     aAllTicks[0].realloc(nMaxMajorTickCount);
599 
600     sal_Int32 nRealMajorTickCount = 0;
601     double* pValue = NULL;
602     for( sal_Int32 nMajorTick=0; nMajorTick<nMaxMajorTickCount; nMajorTick++ )
603     {
604         pValue = this->getMajorTick( nMajorTick );
605         if(!pValue)
606             continue;
607         aAllTicks[0][nRealMajorTickCount] = *pValue;
608         nRealMajorTickCount++;
609     }
610     if(!nRealMajorTickCount)
611         return;
612     aAllTicks[0].realloc(nRealMajorTickCount);
613 
614     if(nDepthCount>0)
615         this->addSubTicks( 1, aAllTicks );
616 
617     //so far we have added all ticks between the outer major tick marks
618     //this was necessary to create sub ticks correctly
619     //now we reduce all ticks to the visible ones that lie between the real borders
620     sal_Int32 nDepth = 0;
621     sal_Int32 nTick = 0;
622     for( nDepth = 0; nDepth < nDepthCount; nDepth++)
623     {
624         sal_Int32 nInvisibleAtLowerBorder = 0;
625         sal_Int32 nInvisibleAtUpperBorder = 0;
626         //we need only to check all ticks within the first major interval at each border
627         sal_Int32 nCheckCount = 1;
628         for(sal_Int32 nN=0; nN<nDepth; nN++)
629         {
630             if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
631                 nCheckCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
632         }
633         uno::Sequence< double >& rTicks = aAllTicks[nDepth];
634         sal_Int32 nCount = rTicks.getLength();
635         //check lower border
636         for( nTick=0; nTick<nCheckCount && nTick<nCount; nTick++)
637         {
638             if( !isVisible( rTicks[nTick] ) )
639                 nInvisibleAtLowerBorder++;
640         }
641         //check upper border
642         for( nTick=nCount-1; nTick>nCount-1-nCheckCount && nTick>=0; nTick--)
643         {
644             if( !isVisible( rTicks[nTick] ) )
645                 nInvisibleAtUpperBorder++;
646         }
647         //resize sequence
648         if( !nInvisibleAtLowerBorder && !nInvisibleAtUpperBorder)
649             continue;
650         if( !nInvisibleAtLowerBorder )
651             rTicks.realloc(nCount-nInvisibleAtUpperBorder);
652         else
653         {
654             sal_Int32 nNewCount = nCount-nInvisibleAtUpperBorder-nInvisibleAtLowerBorder;
655             if(nNewCount<0)
656                 nNewCount=0;
657 
658             uno::Sequence< double > aOldTicks(rTicks);
659             rTicks.realloc(nNewCount);
660             for(nTick = 0; nTick<nNewCount; nTick++)
661                 rTicks[nTick] = aOldTicks[nInvisibleAtLowerBorder+nTick];
662         }
663     }
664 
665     //fill return value
666     rAllTickInfos.resize(aAllTicks.getLength());
667     for( nDepth=0 ;nDepth<aAllTicks.getLength(); nDepth++ )
668     {
669         sal_Int32 nCount = aAllTicks[nDepth].getLength();
670         rAllTickInfos[nDepth].resize( nCount );
671         for(sal_Int32 nN = 0; nN<nCount; nN++)
672         {
673             rAllTickInfos[nDepth][nN].fScaledTickValue = aAllTicks[nDepth][nN];
674         }
675     }
676 }
677 
678 void TickmarkHelper::getAllTicksShifted( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
679 {
680     std::auto_ptr< TickmarkHelper > apShiftedTickmarkHelper( createShiftedTickmarkHelper() );
681     apShiftedTickmarkHelper->getAllTicks( rAllTickInfos );
682 }
683 
684 void TickmarkHelper::addSubTicks( sal_Int32 nDepth, uno::Sequence< uno::Sequence< double > >& rParentTicks ) const
685 {
686     EquidistantTickIter aIter( rParentTicks, m_rIncrement, 0, nDepth-1 );
687     double* pfNextParentTick = aIter.firstValue();
688     if(!pfNextParentTick)
689         return;
690     double fLastParentTick = *pfNextParentTick;
691     pfNextParentTick = aIter.nextValue();
692     if(!pfNextParentTick)
693         return;
694 
695     sal_Int32 nMaxSubTickCount = this->getMaxTickCount( nDepth );
696     if(!nMaxSubTickCount)
697         return;
698 
699     uno::Sequence< double > aSubTicks(nMaxSubTickCount);
700     sal_Int32 nRealSubTickCount = 0;
701     sal_Int32 nIntervalCount = m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
702 
703     double* pValue = NULL;
704     for(; pfNextParentTick; fLastParentTick=*pfNextParentTick, pfNextParentTick = aIter.nextValue())
705     {
706         for( sal_Int32 nPartTick = 1; nPartTick<nIntervalCount; nPartTick++ )
707         {
708             pValue = this->getMinorTick( nPartTick, nDepth
709                         , fLastParentTick, *pfNextParentTick );
710             if(!pValue)
711                 continue;
712 
713             aSubTicks[nRealSubTickCount] = *pValue;
714             nRealSubTickCount++;
715         }
716     }
717 
718     aSubTicks.realloc(nRealSubTickCount);
719     rParentTicks[nDepth] = aSubTicks;
720     if(m_rIncrement.SubIncrements.getLength()>nDepth)
721         addSubTicks( nDepth+1, rParentTicks );
722 }
723 
724 //-----------------------------------------------------------------------------
725 // ___TickmarkHelper_2D___
726 //-----------------------------------------------------------------------------
727 TickmarkHelper_2D::TickmarkHelper_2D(
728           const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement
729           //, double fStrech_SceneToScreen, double fOffset_SceneToScreen )
730           , const B2DVector& rStartScreenPos, const B2DVector& rEndScreenPos
731           , const B2DVector& rAxisLineToLabelLineShift )
732           : TickmarkHelper( rScale, rIncrement )
733           , m_aAxisStartScreenPosition2D(rStartScreenPos)
734           , m_aAxisEndScreenPosition2D(rEndScreenPos)
735           , m_aAxisLineToLabelLineShift(rAxisLineToLabelLineShift)
736           , m_fStrech_LogicToScreen(1.0)
737           , m_fOffset_LogicToScreen(0.0)
738 {
739     double fWidthY = m_fScaledVisibleMax - m_fScaledVisibleMin;
740     if( AxisOrientation_MATHEMATICAL==m_rScale.Orientation )
741     {
742         m_fStrech_LogicToScreen = 1.0/fWidthY;
743         m_fOffset_LogicToScreen = -m_fScaledVisibleMin;
744     }
745     else
746     {
747         B2DVector aSwap(m_aAxisStartScreenPosition2D);
748         m_aAxisStartScreenPosition2D = m_aAxisEndScreenPosition2D;
749         m_aAxisEndScreenPosition2D = aSwap;
750 
751         m_fStrech_LogicToScreen = -1.0/fWidthY;
752         m_fOffset_LogicToScreen = -m_fScaledVisibleMax;
753     }
754 }
755 
756 TickmarkHelper* TickmarkHelper_2D::createShiftedTickmarkHelper() const
757 {
758     ExplicitIncrementData aShiftedIncrement( m_rIncrement );
759     aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0;
760 
761     ::basegfx::B2DVector aStart( m_aAxisStartScreenPosition2D );
762     ::basegfx::B2DVector aEnd( m_aAxisEndScreenPosition2D );
763     if( AxisOrientation_MATHEMATICAL==m_rScale.Orientation )
764         std::swap( aStart, aEnd );
765 
766     return new TickmarkHelper_2D( m_rScale, aShiftedIncrement, aStart, aEnd, m_aAxisLineToLabelLineShift );
767 }
768 
769 TickmarkHelper_2D::~TickmarkHelper_2D()
770 {
771 }
772 
773 bool TickmarkHelper_2D::isHorizontalAxis() const
774 {
775     return ( m_aAxisStartScreenPosition2D.getY() == m_aAxisEndScreenPosition2D.getY() );
776 }
777 bool TickmarkHelper_2D::isVerticalAxis() const
778 {
779     return ( m_aAxisStartScreenPosition2D.getX() == m_aAxisEndScreenPosition2D.getX() );
780 }
781 
782 sal_Int32 TickmarkHelper_2D::getTickScreenDistance( TickIter& rIter )
783 {
784     //return the positive distance between the two first tickmarks in screen values
785     //if there are less than two tickmarks -1 is returned
786 
787     const TickInfo* pFirstTickInfo = rIter.firstInfo();
788     const TickInfo* pSecondTickInfo = rIter.nextInfo();
789     if(!pSecondTickInfo  || !pFirstTickInfo)
790         return -1;
791 
792     return pFirstTickInfo->getScreenDistanceBetweenTicks( *pSecondTickInfo );
793 }
794 
795 B2DVector TickmarkHelper_2D::getTickScreenPosition2D( double fScaledLogicTickValue ) const
796 {
797     B2DVector aRet(m_aAxisStartScreenPosition2D);
798     aRet += (m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D)
799                 *((fScaledLogicTickValue+m_fOffset_LogicToScreen)*m_fStrech_LogicToScreen);
800     return aRet;
801 }
802 
803 void TickmarkHelper_2D::addPointSequenceForTickLine( drawing::PointSequenceSequence& rPoints
804                                 , sal_Int32 nSequenceIndex
805                                 , double fScaledLogicTickValue, double fInnerDirectionSign
806                                 , const TickmarkProperties& rTickmarkProperties
807                                 , bool bPlaceAtLabels ) const
808 {
809     if( fInnerDirectionSign==0.0 )
810         fInnerDirectionSign = 1.0;
811 
812     B2DVector aTickScreenPosition = this->getTickScreenPosition2D(fScaledLogicTickValue);
813     if( bPlaceAtLabels )
814         aTickScreenPosition += m_aAxisLineToLabelLineShift;
815 
816     B2DVector aMainDirection = m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D;
817     aMainDirection.normalize();
818     B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
819     aOrthoDirection *= fInnerDirectionSign;
820     aOrthoDirection.normalize();
821 
822     B2DVector aStart = aTickScreenPosition + aOrthoDirection*rTickmarkProperties.RelativePos;
823     B2DVector aEnd = aStart - aOrthoDirection*rTickmarkProperties.Length;
824 
825     rPoints[nSequenceIndex].realloc(2);
826     rPoints[nSequenceIndex][0].X = static_cast<sal_Int32>(aStart.getX());
827     rPoints[nSequenceIndex][0].Y = static_cast<sal_Int32>(aStart.getY());
828     rPoints[nSequenceIndex][1].X = static_cast<sal_Int32>(aEnd.getX());
829     rPoints[nSequenceIndex][1].Y = static_cast<sal_Int32>(aEnd.getY());
830 }
831 
832 B2DVector TickmarkHelper_2D::getDistanceAxisTickToText( const AxisProperties& rAxisProperties, bool bIncludeFarAwayDistanceIfSo, bool bIncludeSpaceBetweenTickAndText ) const
833 {
834     bool bFarAwayLabels = false;
835     if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START == rAxisProperties.m_eLabelPos
836         || ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END == rAxisProperties.m_eLabelPos )
837         bFarAwayLabels = true;
838 
839     double fInnerDirectionSign = rAxisProperties.m_fInnerDirectionSign;
840     if( fInnerDirectionSign==0.0 )
841         fInnerDirectionSign = 1.0;
842 
843     B2DVector aMainDirection = m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D;
844     aMainDirection.normalize();
845     B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
846     aOrthoDirection *= fInnerDirectionSign;
847     aOrthoDirection.normalize();
848 
849     B2DVector aStart(0,0), aEnd(0,0);
850     if( bFarAwayLabels )
851     {
852         TickmarkProperties aProps( AxisProperties::getBiggestTickmarkProperties() );
853         aStart = aOrthoDirection*aProps.RelativePos;
854         aEnd = aStart - aOrthoDirection*aProps.Length;
855     }
856     else
857     {
858         for( sal_Int32 nN=rAxisProperties.m_aTickmarkPropertiesList.size();nN--;)
859         {
860             const TickmarkProperties& rProps = rAxisProperties.m_aTickmarkPropertiesList[nN];
861             B2DVector aNewStart = aOrthoDirection*rProps.RelativePos;
862             B2DVector aNewEnd = aNewStart - aOrthoDirection*rProps.Length;
863             if(aNewStart.getLength()>aStart.getLength())
864                 aStart=aNewStart;
865             if(aNewEnd.getLength()>aEnd.getLength())
866                 aEnd=aNewEnd;
867         }
868     }
869 
870     B2DVector aLabelDirection(aStart);
871     if( rAxisProperties.m_fInnerDirectionSign != rAxisProperties.m_fLabelDirectionSign )
872         aLabelDirection = aEnd;
873 
874     B2DVector aOrthoLabelDirection(aOrthoDirection);
875     if( rAxisProperties.m_fInnerDirectionSign != rAxisProperties.m_fLabelDirectionSign )
876         aOrthoLabelDirection*=-1.0;
877     aOrthoLabelDirection.normalize();
878     if( bIncludeSpaceBetweenTickAndText )
879         aLabelDirection += aOrthoLabelDirection*AXIS2D_TICKLABELSPACING;
880     if( bFarAwayLabels && bIncludeFarAwayDistanceIfSo )
881         aLabelDirection += m_aAxisLineToLabelLineShift;
882     return aLabelDirection;
883 }
884 
885 void TickmarkHelper_2D::createPointSequenceForAxisMainLine( drawing::PointSequenceSequence& rPoints ) const
886 {
887     rPoints[0].realloc(2);
888     rPoints[0][0].X = static_cast<sal_Int32>(m_aAxisStartScreenPosition2D.getX());
889     rPoints[0][0].Y = static_cast<sal_Int32>(m_aAxisStartScreenPosition2D.getY());
890     rPoints[0][1].X = static_cast<sal_Int32>(m_aAxisEndScreenPosition2D.getX());
891     rPoints[0][1].Y = static_cast<sal_Int32>(m_aAxisEndScreenPosition2D.getY());
892 }
893 
894 void TickmarkHelper_2D::updateScreenValues( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
895 {
896     //get the transformed screen values for all tickmarks in rAllTickInfos
897     ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter       = rAllTickInfos.begin();
898     const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd  = rAllTickInfos.end();
899     for( ; aDepthIter != aDepthEnd; aDepthIter++ )
900     {
901         ::std::vector< TickInfo >::iterator       aTickIter = (*aDepthIter).begin();
902         const ::std::vector< TickInfo >::const_iterator aTickEnd  = (*aDepthIter).end();
903         for( ; aTickIter != aTickEnd; aTickIter++ )
904         {
905             TickInfo& rTickInfo = (*aTickIter);
906             rTickInfo.aTickScreenPosition =
907                 this->getTickScreenPosition2D( rTickInfo.fScaledTickValue );
908         }
909     }
910 }
911 
912 //-----------------------------------------------------------------------------
913 // ___TickmarkHelper_3D___
914 //-----------------------------------------------------------------------------
915 TickmarkHelper_3D::TickmarkHelper_3D(
916           const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement )
917           : TickmarkHelper( rScale, rIncrement )
918 {
919 }
920 
921 TickmarkHelper* TickmarkHelper_3D::createShiftedTickmarkHelper() const
922 {
923     ExplicitIncrementData aShiftedIncrement( m_rIncrement );
924     aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0;
925     return new TickmarkHelper_3D( m_rScale, aShiftedIncrement );
926 }
927 
928 TickmarkHelper_3D::~TickmarkHelper_3D()
929 {
930 }
931 
932 //.............................................................................
933 } //namespace chart
934 //.............................................................................
935