LibreOffice Module chart2 (master) 1
Tickmarks_Equidistant.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21#include <rtl/math.hxx>
22#include <osl/diagnose.h>
23#include <float.h>
24
25#include <limits>
26#include <utility>
27
28namespace chart
29{
30using namespace ::com::sun::star;
31using namespace ::com::sun::star::chart2;
32using namespace ::rtl::math;
33
34//static
36{
37 //the returned value will be <= fMin and on a Major Tick given by rIncrement
38 if(rIncrement.Distance<=0.0)
39 return fMin;
40
41 double fRet = rIncrement.BaseValue +
42 floor( approxSub( fMin, rIncrement.BaseValue )
43 / rIncrement.Distance)
44 *rIncrement.Distance;
45
46 if( fRet > fMin )
47 {
48 if( !approxEqual(fRet, fMin) )
49 fRet -= rIncrement.Distance;
50 }
51 return fRet;
52}
53//static
55{
56 //the returned value will be >= fMax and on a Major Tick given by rIncrement
57 if(rIncrement.Distance<=0.0)
58 return fMax;
59
60 double fRet = rIncrement.BaseValue +
61 floor( approxSub( fMax, rIncrement.BaseValue )
62 / rIncrement.Distance)
63 *rIncrement.Distance;
64
65 if( fRet < fMax )
66 {
67 if( !approxEqual(fRet, fMax) )
68 fRet += rIncrement.Distance;
69 }
70 return fRet;
71}
72
74 ExplicitScaleData aScale, ExplicitIncrementData aIncrement )
75 : m_rScale(std::move( aScale ))
76 , m_rIncrement(std::move( aIncrement ))
77{
78 //@todo: make sure that the scale is valid for the scaling
79
80 m_pfCurrentValues.reset( new double[getTickDepth()] );
81
82 if( m_rScale.Scaling.is() )
83 {
84 m_xInverseScaling = m_rScale.Scaling->getInverseScaling();
85 OSL_ENSURE( m_xInverseScaling.is(), "each Scaling needs to return an inverse Scaling" );
86 }
87
88 double fMin = m_fScaledVisibleMin = m_rScale.Minimum;
89 if( m_xInverseScaling.is() )
90 {
94 }
95
96 double fMax = m_fScaledVisibleMax = m_rScale.Maximum;
97 if( m_xInverseScaling.is() )
98 {
101 fMax = m_fScaledVisibleMax;
102 }
103
106
110 return;
111
114
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( !std::isfinite(m_fOuterMajorTickBorderMin_Scaled) )
118 {
121 }
122 if( !std::isfinite(m_fOuterMajorTickBorderMax_Scaled) )
123 {
126 }
127}
128
130{
131}
132
134{
135 return static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) + 1;
136}
137
138void EquidistantTickFactory::addSubTicks( sal_Int32 nDepth, uno::Sequence< uno::Sequence< double > >& rParentTicks ) const
139{
140 EquidistantTickIter aIter( rParentTicks, m_rIncrement, nDepth-1 );
141 double* pfNextParentTick = aIter.firstValue();
142 if(!pfNextParentTick)
143 return;
144 double fLastParentTick = *pfNextParentTick;
145 pfNextParentTick = aIter.nextValue();
146 if(!pfNextParentTick)
147 return;
148
149 sal_Int32 nMaxSubTickCount = getMaxTickCount( nDepth );
150 if(!nMaxSubTickCount)
151 return;
152
153 uno::Sequence< double > aSubTicks(nMaxSubTickCount);
154 auto pSubTicks = aSubTicks.getArray();
155 sal_Int32 nRealSubTickCount = 0;
156 sal_Int32 nIntervalCount = m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
157
158 double* pValue = nullptr;
159 for(; pfNextParentTick; fLastParentTick=*pfNextParentTick, pfNextParentTick = aIter.nextValue())
160 {
161 for( sal_Int32 nPartTick = 1; nPartTick<nIntervalCount; nPartTick++ )
162 {
163 pValue = getMinorTick( nPartTick, nDepth
164 , fLastParentTick, *pfNextParentTick );
165 if(!pValue)
166 continue;
167
168 pSubTicks[nRealSubTickCount] = *pValue;
169 nRealSubTickCount++;
170 }
171 }
172
173 aSubTicks.realloc(nRealSubTickCount);
174 rParentTicks.getArray()[nDepth] = aSubTicks;
175 if(static_cast<sal_Int32>(m_rIncrement.SubIncrements.size())>nDepth)
176 addSubTicks( nDepth+1, rParentTicks );
177}
178
179sal_Int32 EquidistantTickFactory::getMaxTickCount( sal_Int32 nDepth ) const
180{
181 //return the maximum amount of ticks
182 //possibly open intervals at the two ends of the region are handled as if they were completely visible
183 //(this is necessary for calculating the sub ticks at the borders correctly)
184
185 if( nDepth >= getTickDepth() )
186 return 0;
188 return 0;
189 if( m_rIncrement.Distance<=0.0)
190 return 0;
191
192 double fSub;
194 fSub = approxSub( m_fScaledVisibleMax, m_fScaledVisibleMin );
195 else
196 fSub = approxSub( m_rScale.Maximum, m_rScale.Minimum );
197
198 if (!std::isfinite(fSub))
199 return 0;
200
201 double fIntervalCount = fSub / m_rIncrement.Distance;
202 if (fIntervalCount > std::numeric_limits<sal_Int32>::max())
203 // Interval count too high! Bail out.
204 return 0;
205
206 sal_Int32 nIntervalCount = static_cast<sal_Int32>(fIntervalCount);
207
208 nIntervalCount+=3;
209 for(sal_Int32 nN=0; nN<nDepth-1; nN++)
210 {
211 if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
212 nIntervalCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
213 }
214
215 sal_Int32 nTickCount = nIntervalCount;
216 if(nDepth>0 && m_rIncrement.SubIncrements[nDepth-1].IntervalCount>1)
217 nTickCount = nIntervalCount * (m_rIncrement.SubIncrements[nDepth-1].IntervalCount-1);
218
219 return nTickCount;
220}
221
222double* EquidistantTickFactory::getMajorTick( sal_Int32 nTick ) const
223{
225
227 {
228 if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMax) )
229 return nullptr;
230 }
232 {
233 if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMin) )
234 return nullptr;
235 }
236
237 //return always the value after scaling
240
241 return &m_pfCurrentValues[0];
242}
243
244double* EquidistantTickFactory::getMinorTick( sal_Int32 nTick, sal_Int32 nDepth
245 , double fStartParentTick, double fNextParentTick ) const
246{
247 //check validity of arguments
248 {
249 //OSL_ENSURE( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick");
250 if(fStartParentTick >= fNextParentTick)
251 return nullptr;
252 if(nDepth>static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) || nDepth<=0)
253 return nullptr;
254
255 //subticks are only calculated if they are laying between parent ticks:
256 if(nTick<=0)
257 return nullptr;
258 if(nTick>=m_rIncrement.SubIncrements[nDepth-1].IntervalCount)
259 return nullptr;
260 }
261
262 bool bPostEquidistant = m_rIncrement.SubIncrements[nDepth-1].PostEquidistant;
263
264 double fAdaptedStartParent = fStartParentTick;
265 double fAdaptedNextParent = fNextParentTick;
266
267 if( !bPostEquidistant && m_xInverseScaling.is() )
268 {
269 fAdaptedStartParent = m_xInverseScaling->doScaling(fStartParentTick);
270 fAdaptedNextParent = m_xInverseScaling->doScaling(fNextParentTick);
271 }
272
273 double fDistance = (fAdaptedNextParent - fAdaptedStartParent)/m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
274
275 m_pfCurrentValues[nDepth] = fAdaptedStartParent + nTick*fDistance;
276
277 //return always the value after scaling
278 if(!bPostEquidistant && m_xInverseScaling.is() )
279 m_pfCurrentValues[nDepth] = m_rScale.Scaling->doScaling( m_pfCurrentValues[nDepth] );
280
281 if( !isWithinOuterBorder( m_pfCurrentValues[nDepth] ) )
282 return nullptr;
283
284 return &m_pfCurrentValues[nDepth];
285}
286
287bool EquidistantTickFactory::isWithinOuterBorder( double fScaledValue ) const
288{
289 if(fScaledValue>m_fOuterMajorTickBorderMax_Scaled)
290 return false;
291 if(fScaledValue<m_fOuterMajorTickBorderMin_Scaled)
292 return false;
293
294 return true;
295}
296
297bool EquidistantTickFactory::isVisible( double fScaledValue ) const
298{
299 if(fScaledValue>m_fScaledVisibleMax)
300 {
301 if( !approxEqual(fScaledValue,m_fScaledVisibleMax) )
302 return false;
303 }
304 if(fScaledValue<m_fScaledVisibleMin)
305 {
306 if( !approxEqual(fScaledValue,m_fScaledVisibleMin) )
307 return false;
308 }
309 return true;
310}
311
313{
314 //create point sequences for each tick depth
315 const sal_Int32 nDepthCount = getTickDepth();
316 const sal_Int32 nMaxMajorTickCount = getMaxTickCount(0);
317
318 if (nDepthCount <= 0 || nMaxMajorTickCount <= 0)
319 return;
320
321 uno::Sequence< uno::Sequence< double > > aAllTicks(nDepthCount);
322 auto pAllTicks = aAllTicks.getArray();
323 pAllTicks[0].realloc(nMaxMajorTickCount);
324 auto pAllTicks0 = pAllTicks[0].getArray();
325
326 sal_Int32 nRealMajorTickCount = 0;
327 for( sal_Int32 nMajorTick=0; nMajorTick<nMaxMajorTickCount; nMajorTick++ )
328 {
329 double* pValue = getMajorTick( nMajorTick );
330 if(!pValue)
331 continue;
332 pAllTicks0[nRealMajorTickCount] = *pValue;
333 nRealMajorTickCount++;
334 }
335 if(!nRealMajorTickCount)
336 return;
337 pAllTicks[0].realloc(nRealMajorTickCount);
338
339 addSubTicks(1, aAllTicks);
340
341 //so far we have added all ticks between the outer major tick marks
342 //this was necessary to create sub ticks correctly
343 //now we reduce all ticks to the visible ones that lie between the real borders
344 sal_Int32 nDepth = 0;
345 sal_Int32 nTick = 0;
346 for( nDepth = 0; nDepth < nDepthCount; nDepth++)
347 {
348 sal_Int32 nInvisibleAtLowerBorder = 0;
349 sal_Int32 nInvisibleAtUpperBorder = 0;
350 //we need only to check all ticks within the first major interval at each border
351 sal_Int32 nCheckCount = 1;
352 for(sal_Int32 nN=0; nN<nDepth; nN++)
353 {
354 if( m_rIncrement.SubIncrements[nN].IntervalCount>1 )
355 nCheckCount *= m_rIncrement.SubIncrements[nN].IntervalCount;
356 }
357 uno::Sequence< double >& rTicks = pAllTicks[nDepth];
358 sal_Int32 nCount = rTicks.getLength();
359 //check lower border
360 for( nTick=0; nTick<nCheckCount && nTick<nCount; nTick++)
361 {
362 if( !isVisible( rTicks[nTick] ) )
363 nInvisibleAtLowerBorder++;
364 }
365 //check upper border
366 for( nTick=nCount-1; nTick>nCount-1-nCheckCount && nTick>=0; nTick--)
367 {
368 if( !isVisible( rTicks[nTick] ) )
369 nInvisibleAtUpperBorder++;
370 }
371 //resize sequence
372 if( !nInvisibleAtLowerBorder && !nInvisibleAtUpperBorder)
373 continue;
374 if( !nInvisibleAtLowerBorder )
375 rTicks.realloc(nCount-nInvisibleAtUpperBorder);
376 else
377 {
378 sal_Int32 nNewCount = nCount-nInvisibleAtUpperBorder-nInvisibleAtLowerBorder;
379 if(nNewCount<0)
380 nNewCount=0;
381
382 uno::Sequence< double > aOldTicks(rTicks);
383 rTicks.realloc(nNewCount);
384 auto pTicks = rTicks.getArray();
385 for(nTick = 0; nTick<nNewCount; nTick++)
386 pTicks[nTick] = aOldTicks[nInvisibleAtLowerBorder+nTick];
387 }
388 }
389
390 //fill return value
391 rAllTickInfos.resize(aAllTicks.getLength());
392 for( nDepth=0 ;nDepth<aAllTicks.getLength(); nDepth++ )
393 {
394 sal_Int32 nCount = aAllTicks[nDepth].getLength();
395
396 TickInfoArrayType& rTickInfoVector = rAllTickInfos[nDepth];
397 rTickInfoVector.clear();
398 rTickInfoVector.reserve( nCount );
399 for(sal_Int32 nN = 0; nN<nCount; nN++)
400 {
401 TickInfo aTickInfo(m_xInverseScaling);
402 aTickInfo.fScaledTickValue = aAllTicks[nDepth][nN];
403 rTickInfoVector.push_back(aTickInfo);
404 }
405 }
406}
407
409{
410 ExplicitIncrementData aShiftedIncrement( m_rIncrement );
411 aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0;
412 EquidistantTickFactory( m_rScale, aShiftedIncrement ).getAllTicks(rAllTickInfos);
413}
414
416 , const ExplicitIncrementData& rIncrement
417 , sal_Int32 nMaxDepth )
418 : m_pSimpleTicks(&rTicks)
419 , m_pInfoTicks(nullptr)
420 , m_rIncrement(rIncrement)
421 , m_nMaxDepth(0)
422 , m_nTickCount(0)
423 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
424{
425 initIter( nMaxDepth );
426}
427
429 , const ExplicitIncrementData& rIncrement
430 , sal_Int32 nMaxDepth )
431 : m_pSimpleTicks(nullptr)
432 , m_pInfoTicks(&rTicks)
433 , m_rIncrement(rIncrement)
434 , m_nMaxDepth(0)
435 , m_nTickCount(0)
436 , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 )
437{
438 initIter( nMaxDepth );
439}
440
441void EquidistantTickIter::initIter( sal_Int32 nMaxDepth )
442{
443 m_nMaxDepth = nMaxDepth;
444 if(nMaxDepth<0 || m_nMaxDepth>getMaxDepth())
446
447 sal_Int32 nDepth = 0;
448 for( nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
449 m_nTickCount += getTickCount(nDepth);
450
451 if(!m_nTickCount)
452 return;
453
454 m_pnPositions.reset( new sal_Int32[m_nMaxDepth+1] );
455
456 m_pnPreParentCount.reset( new sal_Int32[m_nMaxDepth+1] );
457 m_pbIntervalFinished.reset( new bool[m_nMaxDepth+1] );
458 m_pnPreParentCount[0] = 0;
459 m_pbIntervalFinished[0] = false;
460 double fParentValue = getTickValue(0,0);
461 for( nDepth = 1; nDepth<=m_nMaxDepth ;nDepth++ )
462 {
463 m_pbIntervalFinished[nDepth] = false;
464
465 sal_Int32 nPreParentCount = 0;
466 sal_Int32 nCount = getTickCount(nDepth);
467 for(sal_Int32 nN = 0; nN<nCount; nN++)
468 {
469 if(getTickValue(nDepth,nN) < fParentValue)
470 nPreParentCount++;
471 else
472 break;
473 }
474 m_pnPreParentCount[nDepth] = nPreParentCount;
475 if(nCount)
476 {
477 double fNextParentValue = getTickValue(nDepth,0);
478 if( fNextParentValue < fParentValue )
479 fParentValue = fNextParentValue;
480 }
481 }
482}
483
485{
486}
487
489{
490 //find the depth of the first visible tickmark:
491 //it is the depth of the smallest value
492 sal_Int32 nReturnDepth=0;
493 double fMinValue = DBL_MAX;
494 for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
495 {
496 sal_Int32 nCount = getTickCount(nDepth);
497 if( !nCount )
498 continue;
499 double fThisValue = getTickValue(nDepth,0);
500 if(fThisValue<fMinValue)
501 {
502 nReturnDepth = nDepth;
503 fMinValue = fThisValue;
504 }
505 }
506 return nReturnDepth;
507}
508
510{
511 if( gotoFirst() )
512 {
514 return &m_fCurrentValue;
515 }
516 return nullptr;
517}
518
520{
521 if( m_pInfoTicks && gotoFirst() )
522 return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
523 return nullptr;
524}
525
526sal_Int32 EquidistantTickIter::getIntervalCount( sal_Int32 nDepth )
527{
528 if(nDepth>static_cast<sal_Int32>(m_rIncrement.SubIncrements.size()) || nDepth<0)
529 return 0;
530
531 if(!nDepth)
532 return m_nTickCount;
533
534 return m_rIncrement.SubIncrements[nDepth-1].IntervalCount;
535}
536
538{
539 if(!m_nCurrentDepth)
540 return false;
541 sal_Int32 nIntervalCount = getIntervalCount( m_nCurrentDepth );
542 if(!nIntervalCount || nIntervalCount == 1)
543 return true;
545 return false;
546 sal_Int32 nPos = m_pnPositions[m_nCurrentDepth]+1;
548 nPos += nIntervalCount-1 - m_pnPreParentCount[m_nCurrentDepth];
549 bool bRet = nPos && nPos % (nIntervalCount-1) == 0;
552 bRet = true;
553 return bRet;
554}
555
557{
558 if( m_nMaxDepth<0 )
559 return false;
560 if( !m_nTickCount )
561 return false;
562
563 for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ )
564 m_pnPositions[nDepth] = -1;
565
566 m_nCurrentPos = 0;
569 return true;
570}
571
573{
574 if( m_nCurrentPos < 0 )
575 return false;
577
579 return false;
580
582 {
583 do
584 {
587 }
588 while( m_nCurrentDepth && isAtLastPartTick() );
589 }
591 {
592 do
593 {
595 }
597 }
600 return true;
601}
602
604{
605 if( gotoNext() )
606 {
608 return &m_fCurrentValue;
609 }
610 return nullptr;
611}
612
614{
615 if( m_pInfoTicks && gotoNext() &&
616 static_cast< sal_Int32 >(
618 {
619 return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]];
620 }
621 return nullptr;
622}
623
624} //namespace chart
625
626/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
sal_Int32 nPos
css::uno::Reference< css::chart2::XScaling > m_xInverseScaling
static double getMinimumAtIncrement(double fMin, const ExplicitIncrementData &rIncrement)
bool isWithinOuterBorder(double fScaledValue) const
std::unique_ptr< double[]> m_pfCurrentValues
void getAllTicksShifted(TickInfoArraysType &rAllTickInfos) const
double * getMinorTick(sal_Int32 nTick, sal_Int32 nDepth, double fStartParentTick, double fNextParentTick) const
void addSubTicks(sal_Int32 nDepth, css::uno::Sequence< css::uno::Sequence< double > > &rParentTicks) const
static double getMaximumAtIncrement(double fMax, const ExplicitIncrementData &rIncrement)
bool isVisible(double fValue) const
EquidistantTickFactory(ExplicitScaleData aScale, ExplicitIncrementData aIncrement)
void getAllTicks(TickInfoArraysType &rAllTickInfos) const
sal_Int32 getMaxTickCount(sal_Int32 nDepth) const
double * getMajorTick(sal_Int32 nTick) const
sal_Int32 getIntervalCount(sal_Int32 nDepth)
double getTickValue(sal_Int32 nDepth, sal_Int32 nIndex) const
std::unique_ptr< sal_Int32[]> m_pnPreParentCount
virtual TickInfo * nextInfo() override
const ExplicitIncrementData & m_rIncrement
void initIter(sal_Int32 nMaxDepth)
EquidistantTickIter(const css::uno::Sequence< css::uno::Sequence< double > > &rTicks, const ExplicitIncrementData &rIncrement, sal_Int32 nMaxDepth)
virtual TickInfo * firstInfo() override
std::unique_ptr< sal_Int32[]> m_pnPositions
std::unique_ptr< bool[]> m_pbIntervalFinished
sal_Int32 getTickCount(sal_Int32 nDepth) const
int nCount
size
std::vector< TickInfoArrayType > TickInfoArraysType
Definition: Tickmarks.hxx:59
std::vector< TickInfo > TickInfoArrayType
Definition: Tickmarks.hxx:58
sal_Int32 m_nCurrentPos
describes how tickmarks are positioned on the scale of an axis.
std::vector< ExplicitSubIncrement > SubIncrements
<member>SubIncrements</member> describes the positioning of further sub tickmarks on the scale of an ...
double Distance
the other members are for not date-time axis
bool PostEquidistant
<member>PostEquidistant</member> rules whether the member <member>Distance</member> describes a dista...
double BaseValue
The <member>BaseValue</member> gives a starting point on the scale to which all further main tickmark...
This structure contains the explicit values for a scale like Minimum and Maximum.
css::uno::Reference< css::chart2::XScaling > Scaling
double fScaledTickValue
Definition: Tickmarks.hxx:37