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