1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "Tickmarks_Equidistant.hxx"
21 #include "ViewDefines.hxx"
22 #include <rtl/math.hxx>
28 using namespace ::com::sun::star
;
29 using namespace ::com::sun::star::chart2
;
30 using namespace ::rtl::math
;
31 using ::basegfx::B2DVector
;
34 double EquidistantTickFactory::getMinimumAtIncrement( double fMin
, const ExplicitIncrementData
& rIncrement
)
36 //the returned value will be <= fMin and on a Major Tick given by rIncrement
37 if(rIncrement
.Distance
<=0.0)
40 double fRet
= rIncrement
.BaseValue
+
41 floor( approxSub( fMin
, rIncrement
.BaseValue
)
42 / rIncrement
.Distance
)
47 if( !approxEqual(fRet
, fMin
) )
48 fRet
-= rIncrement
.Distance
;
53 double EquidistantTickFactory::getMaximumAtIncrement( double fMax
, const ExplicitIncrementData
& rIncrement
)
55 //the returned value will be >= fMax and on a Major Tick given by rIncrement
56 if(rIncrement
.Distance
<=0.0)
59 double fRet
= rIncrement
.BaseValue
+
60 floor( approxSub( fMax
, rIncrement
.BaseValue
)
61 / rIncrement
.Distance
)
66 if( !approxEqual(fRet
, fMax
) )
67 fRet
+= rIncrement
.Distance
;
72 EquidistantTickFactory::EquidistantTickFactory(
73 const ExplicitScaleData
& rScale
, const ExplicitIncrementData
& rIncrement
)
75 , m_rIncrement( rIncrement
)
76 , m_xInverseScaling(NULL
)
77 , m_pfCurrentValues(NULL
)
79 //@todo: make sure that the scale is valid for the scaling
81 m_pfCurrentValues
= new double[getTickDepth()];
83 if( m_rScale
.Scaling
.is() )
85 m_xInverseScaling
= m_rScale
.Scaling
->getInverseScaling();
86 OSL_ENSURE( m_xInverseScaling
.is(), "each Scaling needs to return a inverse Scaling" );
89 double fMin
= m_fScaledVisibleMin
= m_rScale
.Minimum
;
90 if( m_xInverseScaling
.is() )
92 m_fScaledVisibleMin
= m_rScale
.Scaling
->doScaling(m_fScaledVisibleMin
);
93 if(m_rIncrement
.PostEquidistant
)
94 fMin
= m_fScaledVisibleMin
;
97 double fMax
= m_fScaledVisibleMax
= m_rScale
.Maximum
;
98 if( m_xInverseScaling
.is() )
100 m_fScaledVisibleMax
= m_rScale
.Scaling
->doScaling(m_fScaledVisibleMax
);
101 if(m_rIncrement
.PostEquidistant
)
102 fMax
= m_fScaledVisibleMax
;
105 m_fOuterMajorTickBorderMin
= EquidistantTickFactory::getMinimumAtIncrement( fMin
, m_rIncrement
);
106 m_fOuterMajorTickBorderMax
= EquidistantTickFactory::getMaximumAtIncrement( fMax
, m_rIncrement
);
108 m_fOuterMajorTickBorderMin_Scaled
= m_fOuterMajorTickBorderMin
;
109 m_fOuterMajorTickBorderMax_Scaled
= m_fOuterMajorTickBorderMax
;
110 if(!m_rIncrement
.PostEquidistant
&& m_xInverseScaling
.is() )
112 m_fOuterMajorTickBorderMin_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMin
);
113 m_fOuterMajorTickBorderMax_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMax
);
115 //check validity of new range: m_fOuterMajorTickBorderMin <-> m_fOuterMajorTickBorderMax
116 //it is assumed here, that the original range in the given Scale is valid
117 if( !rtl::math::isFinite(m_fOuterMajorTickBorderMin_Scaled
) )
119 m_fOuterMajorTickBorderMin
+= m_rIncrement
.Distance
;
120 m_fOuterMajorTickBorderMin_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMin
);
122 if( !rtl::math::isFinite(m_fOuterMajorTickBorderMax_Scaled
) )
124 m_fOuterMajorTickBorderMax
-= m_rIncrement
.Distance
;
125 m_fOuterMajorTickBorderMax_Scaled
= m_rScale
.Scaling
->doScaling(m_fOuterMajorTickBorderMax
);
130 EquidistantTickFactory::~EquidistantTickFactory()
132 delete[] m_pfCurrentValues
;
135 sal_Int32
EquidistantTickFactory::getTickDepth() const
137 return static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size()) + 1;
140 void EquidistantTickFactory::addSubTicks( sal_Int32 nDepth
, uno::Sequence
< uno::Sequence
< double > >& rParentTicks
) const
142 EquidistantTickIter
aIter( rParentTicks
, m_rIncrement
, 0, nDepth
-1 );
143 double* pfNextParentTick
= aIter
.firstValue();
144 if(!pfNextParentTick
)
146 double fLastParentTick
= *pfNextParentTick
;
147 pfNextParentTick
= aIter
.nextValue();
148 if(!pfNextParentTick
)
151 sal_Int32 nMaxSubTickCount
= this->getMaxTickCount( nDepth
);
152 if(!nMaxSubTickCount
)
155 uno::Sequence
< double > aSubTicks(nMaxSubTickCount
);
156 sal_Int32 nRealSubTickCount
= 0;
157 sal_Int32 nIntervalCount
= m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
;
159 double* pValue
= NULL
;
160 for(; pfNextParentTick
; fLastParentTick
=*pfNextParentTick
, pfNextParentTick
= aIter
.nextValue())
162 for( sal_Int32 nPartTick
= 1; nPartTick
<nIntervalCount
; nPartTick
++ )
164 pValue
= this->getMinorTick( nPartTick
, nDepth
165 , fLastParentTick
, *pfNextParentTick
);
169 aSubTicks
[nRealSubTickCount
] = *pValue
;
174 aSubTicks
.realloc(nRealSubTickCount
);
175 rParentTicks
[nDepth
] = aSubTicks
;
176 if(static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size())>nDepth
)
177 addSubTicks( nDepth
+1, rParentTicks
);
180 sal_Int32
EquidistantTickFactory::getMaxTickCount( sal_Int32 nDepth
) const
182 //return the maximum amount of ticks
183 //possibly open intervals at the two ends of the region are handled as if they were completely visible
184 //(this is necessary for calculating the sub ticks at the borders correctly)
186 if( nDepth
>= getTickDepth() )
188 if( m_fOuterMajorTickBorderMax
< m_fOuterMajorTickBorderMin
)
190 if( m_rIncrement
.Distance
<=0.0)
194 if(m_rIncrement
.PostEquidistant
)
195 fSub
= approxSub( m_fScaledVisibleMax
, m_fScaledVisibleMin
);
197 fSub
= approxSub( m_rScale
.Maximum
, m_rScale
.Minimum
);
202 double fIntervalCount
= fSub
/ m_rIncrement
.Distance
;
203 if (fIntervalCount
> std::numeric_limits
<sal_Int32
>::max())
204 // Interval count too high! Bail out.
207 sal_Int32 nIntervalCount
= static_cast<sal_Int32
>(fIntervalCount
);
210 for(sal_Int32 nN
=0; nN
<nDepth
-1; nN
++)
212 if( m_rIncrement
.SubIncrements
[nN
].IntervalCount
>1 )
213 nIntervalCount
*= m_rIncrement
.SubIncrements
[nN
].IntervalCount
;
216 sal_Int32 nTickCount
= nIntervalCount
;
217 if(nDepth
>0 && m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
>1)
218 nTickCount
= nIntervalCount
* (m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
-1);
223 double* EquidistantTickFactory::getMajorTick( sal_Int32 nTick
) const
225 m_pfCurrentValues
[0] = m_fOuterMajorTickBorderMin
+ nTick
*m_rIncrement
.Distance
;
227 if(m_pfCurrentValues
[0]>m_fOuterMajorTickBorderMax
)
229 if( !approxEqual(m_pfCurrentValues
[0],m_fOuterMajorTickBorderMax
) )
232 if(m_pfCurrentValues
[0]<m_fOuterMajorTickBorderMin
)
234 if( !approxEqual(m_pfCurrentValues
[0],m_fOuterMajorTickBorderMin
) )
238 //return always the value after scaling
239 if(!m_rIncrement
.PostEquidistant
&& m_xInverseScaling
.is() )
240 m_pfCurrentValues
[0] = m_rScale
.Scaling
->doScaling( m_pfCurrentValues
[0] );
242 return &m_pfCurrentValues
[0];
245 double* EquidistantTickFactory::getMinorTick( sal_Int32 nTick
, sal_Int32 nDepth
246 , double fStartParentTick
, double fNextParentTick
) const
248 //check validity of arguments
250 //OSL_ENSURE( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick");
251 if(fStartParentTick
>= fNextParentTick
)
253 if(nDepth
>static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size()) || nDepth
<=0)
256 //subticks are only calculated if they are laying between parent ticks:
259 if(nTick
>=m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
)
263 bool bPostEquidistant
= m_rIncrement
.SubIncrements
[nDepth
-1].PostEquidistant
;
265 double fAdaptedStartParent
= fStartParentTick
;
266 double fAdaptedNextParent
= fNextParentTick
;
268 if( !bPostEquidistant
&& m_xInverseScaling
.is() )
270 fAdaptedStartParent
= m_xInverseScaling
->doScaling(fStartParentTick
);
271 fAdaptedNextParent
= m_xInverseScaling
->doScaling(fNextParentTick
);
274 double fDistance
= (fAdaptedNextParent
- fAdaptedStartParent
)/m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
;
276 m_pfCurrentValues
[nDepth
] = fAdaptedStartParent
+ nTick
*fDistance
;
278 //return always the value after scaling
279 if(!bPostEquidistant
&& m_xInverseScaling
.is() )
280 m_pfCurrentValues
[nDepth
] = m_rScale
.Scaling
->doScaling( m_pfCurrentValues
[nDepth
] );
282 if( !isWithinOuterBorder( m_pfCurrentValues
[nDepth
] ) )
285 return &m_pfCurrentValues
[nDepth
];
288 bool EquidistantTickFactory::isWithinOuterBorder( double fScaledValue
) const
290 if(fScaledValue
>m_fOuterMajorTickBorderMax_Scaled
)
292 if(fScaledValue
<m_fOuterMajorTickBorderMin_Scaled
)
298 bool EquidistantTickFactory::isVisible( double fScaledValue
) const
300 if(fScaledValue
>m_fScaledVisibleMax
)
302 if( !approxEqual(fScaledValue
,m_fScaledVisibleMax
) )
305 if(fScaledValue
<m_fScaledVisibleMin
)
307 if( !approxEqual(fScaledValue
,m_fScaledVisibleMin
) )
313 void EquidistantTickFactory::getAllTicks( TickInfoArraysType
& rAllTickInfos
) const
315 uno::Sequence
< uno::Sequence
< double > > aAllTicks
;
317 //create point sequences for each tick depth
318 sal_Int32 nDepthCount
= this->getTickDepth();
319 sal_Int32 nMaxMajorTickCount
= this->getMaxTickCount( 0 );
321 if (nDepthCount
<= 0 || nMaxMajorTickCount
<= 0)
324 aAllTicks
.realloc(nDepthCount
);
325 aAllTicks
[0].realloc(nMaxMajorTickCount
);
327 sal_Int32 nRealMajorTickCount
= 0;
328 for( sal_Int32 nMajorTick
=0; nMajorTick
<nMaxMajorTickCount
; nMajorTick
++ )
330 double* pValue
= this->getMajorTick( nMajorTick
);
333 aAllTicks
[0][nRealMajorTickCount
] = *pValue
;
334 nRealMajorTickCount
++;
336 if(!nRealMajorTickCount
)
338 aAllTicks
[0].realloc(nRealMajorTickCount
);
341 this->addSubTicks( 1, aAllTicks
);
343 //so far we have added all ticks between the outer major tick marks
344 //this was necessary to create sub ticks correctly
345 //now we reduce all ticks to the visible ones that lie between the real borders
346 sal_Int32 nDepth
= 0;
348 for( nDepth
= 0; nDepth
< nDepthCount
; nDepth
++)
350 sal_Int32 nInvisibleAtLowerBorder
= 0;
351 sal_Int32 nInvisibleAtUpperBorder
= 0;
352 //we need only to check all ticks within the first major interval at each border
353 sal_Int32 nCheckCount
= 1;
354 for(sal_Int32 nN
=0; nN
<nDepth
; nN
++)
356 if( m_rIncrement
.SubIncrements
[nN
].IntervalCount
>1 )
357 nCheckCount
*= m_rIncrement
.SubIncrements
[nN
].IntervalCount
;
359 uno::Sequence
< double >& rTicks
= aAllTicks
[nDepth
];
360 sal_Int32 nCount
= rTicks
.getLength();
362 for( nTick
=0; nTick
<nCheckCount
&& nTick
<nCount
; nTick
++)
364 if( !isVisible( rTicks
[nTick
] ) )
365 nInvisibleAtLowerBorder
++;
368 for( nTick
=nCount
-1; nTick
>nCount
-1-nCheckCount
&& nTick
>=0; nTick
--)
370 if( !isVisible( rTicks
[nTick
] ) )
371 nInvisibleAtUpperBorder
++;
374 if( !nInvisibleAtLowerBorder
&& !nInvisibleAtUpperBorder
)
376 if( !nInvisibleAtLowerBorder
)
377 rTicks
.realloc(nCount
-nInvisibleAtUpperBorder
);
380 sal_Int32 nNewCount
= nCount
-nInvisibleAtUpperBorder
-nInvisibleAtLowerBorder
;
384 uno::Sequence
< double > aOldTicks(rTicks
);
385 rTicks
.realloc(nNewCount
);
386 for(nTick
= 0; nTick
<nNewCount
; nTick
++)
387 rTicks
[nTick
] = aOldTicks
[nInvisibleAtLowerBorder
+nTick
];
392 rAllTickInfos
.resize(aAllTicks
.getLength());
393 for( nDepth
=0 ;nDepth
<aAllTicks
.getLength(); nDepth
++ )
395 sal_Int32 nCount
= aAllTicks
[nDepth
].getLength();
397 TickInfoArrayType
& rTickInfoVector
= rAllTickInfos
[nDepth
];
398 rTickInfoVector
.clear();
399 rTickInfoVector
.reserve( nCount
);
400 for(sal_Int32 nN
= 0; nN
<nCount
; nN
++)
402 TickInfo
aTickInfo(m_xInverseScaling
);
403 aTickInfo
.fScaledTickValue
= aAllTicks
[nDepth
][nN
];
404 rTickInfoVector
.push_back(aTickInfo
);
409 void EquidistantTickFactory::getAllTicksShifted( TickInfoArraysType
& rAllTickInfos
) const
411 ExplicitIncrementData
aShiftedIncrement( m_rIncrement
);
412 aShiftedIncrement
.BaseValue
= m_rIncrement
.BaseValue
-m_rIncrement
.Distance
/2.0;
413 EquidistantTickFactory( m_rScale
, aShiftedIncrement
).getAllTicks(rAllTickInfos
);
416 EquidistantTickIter::EquidistantTickIter( const uno::Sequence
< uno::Sequence
< double > >& rTicks
417 , const ExplicitIncrementData
& rIncrement
418 , sal_Int32 nMinDepth
, sal_Int32 nMaxDepth
)
419 : m_pSimpleTicks(&rTicks
)
421 , m_rIncrement(rIncrement
)
423 , m_nTickCount(0), m_pnPositions(NULL
)
424 , m_pnPreParentCount(NULL
), m_pbIntervalFinished(NULL
)
425 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
427 initIter( nMinDepth
, nMaxDepth
);
430 EquidistantTickIter::EquidistantTickIter( TickInfoArraysType
& rTicks
431 , const ExplicitIncrementData
& rIncrement
432 , sal_Int32 nMinDepth
, sal_Int32 nMaxDepth
)
433 : m_pSimpleTicks(NULL
)
434 , m_pInfoTicks(&rTicks
)
435 , m_rIncrement(rIncrement
)
437 , m_nTickCount(0), m_pnPositions(NULL
)
438 , m_pnPreParentCount(NULL
), m_pbIntervalFinished(NULL
)
439 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
441 initIter( nMinDepth
, nMaxDepth
);
444 void EquidistantTickIter::initIter( sal_Int32
/*nMinDepth*/, sal_Int32 nMaxDepth
)
446 m_nMaxDepth
= nMaxDepth
;
447 if(nMaxDepth
<0 || m_nMaxDepth
>getMaxDepth())
448 m_nMaxDepth
=getMaxDepth();
450 sal_Int32 nDepth
= 0;
451 for( nDepth
= 0; nDepth
<=m_nMaxDepth
;nDepth
++ )
452 m_nTickCount
+= getTickCount(nDepth
);
457 m_pnPositions
= new sal_Int32
[m_nMaxDepth
+1];
459 m_pnPreParentCount
= new sal_Int32
[m_nMaxDepth
+1];
460 m_pbIntervalFinished
= new bool[m_nMaxDepth
+1];
461 m_pnPreParentCount
[0] = 0;
462 m_pbIntervalFinished
[0] = false;
463 double fParentValue
= getTickValue(0,0);
464 for( nDepth
= 1; nDepth
<=m_nMaxDepth
;nDepth
++ )
466 m_pbIntervalFinished
[nDepth
] = false;
468 sal_Int32 nPreParentCount
= 0;
469 sal_Int32 nCount
= getTickCount(nDepth
);
470 for(sal_Int32 nN
= 0; nN
<nCount
; nN
++)
472 if(getTickValue(nDepth
,nN
) < fParentValue
)
477 m_pnPreParentCount
[nDepth
] = nPreParentCount
;
480 double fNextParentValue
= getTickValue(nDepth
,0);
481 if( fNextParentValue
< fParentValue
)
482 fParentValue
= fNextParentValue
;
487 EquidistantTickIter::~EquidistantTickIter()
489 delete[] m_pnPositions
;
490 delete[] m_pnPreParentCount
;
491 delete[] m_pbIntervalFinished
;
494 sal_Int32
EquidistantTickIter::getStartDepth() const
496 //find the depth of the first visible tickmark:
497 //it is the depth of the smallest value
498 sal_Int32 nReturnDepth
=0;
499 double fMinValue
= DBL_MAX
;
500 for(sal_Int32 nDepth
= 0; nDepth
<=m_nMaxDepth
;nDepth
++ )
502 sal_Int32 nCount
= getTickCount(nDepth
);
505 double fThisValue
= getTickValue(nDepth
,0);
506 if(fThisValue
<fMinValue
)
508 nReturnDepth
= nDepth
;
509 fMinValue
= fThisValue
;
515 double* EquidistantTickIter::firstValue()
519 m_fCurrentValue
= getTickValue(m_nCurrentDepth
, m_pnPositions
[m_nCurrentDepth
]);
520 return &m_fCurrentValue
;
525 TickInfo
* EquidistantTickIter::firstInfo()
527 if( m_pInfoTicks
&& gotoFirst() )
528 return &(*m_pInfoTicks
)[m_nCurrentDepth
][m_pnPositions
[m_nCurrentDepth
]];
532 sal_Int32
EquidistantTickIter::getIntervalCount( sal_Int32 nDepth
)
534 if(nDepth
>static_cast<sal_Int32
>(m_rIncrement
.SubIncrements
.size()) || nDepth
<0)
540 return m_rIncrement
.SubIncrements
[nDepth
-1].IntervalCount
;
543 bool EquidistantTickIter::isAtLastPartTick()
547 sal_Int32 nIntervalCount
= getIntervalCount( m_nCurrentDepth
);
548 if(!nIntervalCount
|| nIntervalCount
== 1)
550 if( m_pbIntervalFinished
[m_nCurrentDepth
] )
552 sal_Int32 nPos
= m_pnPositions
[m_nCurrentDepth
]+1;
553 if(m_pnPreParentCount
[m_nCurrentDepth
])
554 nPos
+= nIntervalCount
-1 - m_pnPreParentCount
[m_nCurrentDepth
];
555 bool bRet
= nPos
&& nPos
% (nIntervalCount
-1) == 0;
556 if(!nPos
&& !m_pnPreParentCount
[m_nCurrentDepth
]
557 && m_pnPositions
[m_nCurrentDepth
-1]==-1 )
562 bool EquidistantTickIter::gotoFirst()
569 for(sal_Int32 nDepth
= 0; nDepth
<=m_nMaxDepth
;nDepth
++ )
570 m_pnPositions
[nDepth
] = -1;
573 m_nCurrentDepth
= getStartDepth();
574 m_pnPositions
[m_nCurrentDepth
] = 0;
578 bool EquidistantTickIter::gotoNext()
580 if( m_nCurrentPos
< 0 )
584 if( m_nCurrentPos
>= m_nTickCount
)
587 if( m_nCurrentDepth
==m_nMaxDepth
&& isAtLastPartTick() )
591 m_pbIntervalFinished
[m_nCurrentDepth
] = true;
594 while( m_nCurrentDepth
&& isAtLastPartTick() );
596 else if( m_nCurrentDepth
<m_nMaxDepth
)
602 while( m_nCurrentDepth
<m_nMaxDepth
);
604 m_pbIntervalFinished
[m_nCurrentDepth
] = false;
605 m_pnPositions
[m_nCurrentDepth
] = m_pnPositions
[m_nCurrentDepth
]+1;
609 double* EquidistantTickIter::nextValue()
613 m_fCurrentValue
= getTickValue(m_nCurrentDepth
, m_pnPositions
[m_nCurrentDepth
]);
614 return &m_fCurrentValue
;
619 TickInfo
* EquidistantTickIter::nextInfo()
621 if( m_pInfoTicks
&& gotoNext() &&
622 static_cast< sal_Int32
>(
623 (*m_pInfoTicks
)[m_nCurrentDepth
].size()) > m_pnPositions
[m_nCurrentDepth
] )
625 return &(*m_pInfoTicks
)[m_nCurrentDepth
][m_pnPositions
[m_nCurrentDepth
]];
632 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */