/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * for a copy of the LGPLv3 License. * ************************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_chart2.hxx" #include "Tickmarks_Equidistant.hxx" #include "ViewDefines.hxx" #include #include #include //............................................................................. namespace chart { //............................................................................. using namespace ::com::sun::star; using namespace ::com::sun::star::chart2; using namespace ::rtl::math; using ::basegfx::B2DVector; //static double EquidistantTickFactory::getMinimumAtIncrement( double fMin, const ExplicitIncrementData& rIncrement ) { //the returned value will be <= fMin and on a Major Tick given by rIncrement if(rIncrement.Distance<=0.0) return fMin; double fRet = rIncrement.BaseValue + floor( approxSub( fMin, rIncrement.BaseValue ) / rIncrement.Distance) *rIncrement.Distance; if( fRet > fMin ) { if( !approxEqual(fRet, fMin) ) fRet -= rIncrement.Distance; } return fRet; } //static double EquidistantTickFactory::getMaximumAtIncrement( double fMax, const ExplicitIncrementData& rIncrement ) { //the returned value will be >= fMax and on a Major Tick given by rIncrement if(rIncrement.Distance<=0.0) return fMax; double fRet = rIncrement.BaseValue + floor( approxSub( fMax, rIncrement.BaseValue ) / rIncrement.Distance) *rIncrement.Distance; if( fRet < fMax ) { if( !approxEqual(fRet, fMax) ) fRet += rIncrement.Distance; } return fRet; } EquidistantTickFactory::EquidistantTickFactory( const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement ) : m_rScale( rScale ) , m_rIncrement( rIncrement ) , m_xInverseScaling(NULL) , m_pfCurrentValues(NULL) { //@todo: make sure that the scale is valid for the scaling m_pfCurrentValues = new double[getTickDepth()]; if( m_rScale.Scaling.is() ) { m_xInverseScaling = m_rScale.Scaling->getInverseScaling(); OSL_ENSURE( m_xInverseScaling.is(), "each Scaling needs to return a inverse Scaling" ); } double fMin = m_fScaledVisibleMin = m_rScale.Minimum; if( m_xInverseScaling.is() ) { m_fScaledVisibleMin = m_rScale.Scaling->doScaling(m_fScaledVisibleMin); if(m_rIncrement.PostEquidistant ) fMin = m_fScaledVisibleMin; } double fMax = m_fScaledVisibleMax = m_rScale.Maximum; if( m_xInverseScaling.is() ) { m_fScaledVisibleMax = m_rScale.Scaling->doScaling(m_fScaledVisibleMax); if(m_rIncrement.PostEquidistant ) fMax = m_fScaledVisibleMax; } //-- m_fOuterMajorTickBorderMin = EquidistantTickFactory::getMinimumAtIncrement( fMin, m_rIncrement ); m_fOuterMajorTickBorderMax = EquidistantTickFactory::getMaximumAtIncrement( fMax, m_rIncrement ); //-- m_fOuterMajorTickBorderMin_Scaled = m_fOuterMajorTickBorderMin; m_fOuterMajorTickBorderMax_Scaled = m_fOuterMajorTickBorderMax; if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() ) { m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin); m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax); //check validity of new range: m_fOuterMajorTickBorderMin <-> m_fOuterMajorTickBorderMax //it is assumed here, that the original range in the given Scale is valid if( !rtl::math::isFinite(m_fOuterMajorTickBorderMin_Scaled) ) { m_fOuterMajorTickBorderMin += m_rIncrement.Distance; m_fOuterMajorTickBorderMin_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMin); } if( !rtl::math::isFinite(m_fOuterMajorTickBorderMax_Scaled) ) { m_fOuterMajorTickBorderMax -= m_rIncrement.Distance; m_fOuterMajorTickBorderMax_Scaled = m_rScale.Scaling->doScaling(m_fOuterMajorTickBorderMax); } } } EquidistantTickFactory::~EquidistantTickFactory() { delete[] m_pfCurrentValues; } sal_Int32 EquidistantTickFactory::getTickDepth() const { return static_cast(m_rIncrement.SubIncrements.size()) + 1; } void EquidistantTickFactory::addSubTicks( sal_Int32 nDepth, uno::Sequence< uno::Sequence< double > >& rParentTicks ) const { EquidistantTickIter aIter( rParentTicks, m_rIncrement, 0, nDepth-1 ); double* pfNextParentTick = aIter.firstValue(); if(!pfNextParentTick) return; double fLastParentTick = *pfNextParentTick; pfNextParentTick = aIter.nextValue(); if(!pfNextParentTick) return; sal_Int32 nMaxSubTickCount = this->getMaxTickCount( nDepth ); if(!nMaxSubTickCount) return; uno::Sequence< double > aSubTicks(nMaxSubTickCount); sal_Int32 nRealSubTickCount = 0; sal_Int32 nIntervalCount = m_rIncrement.SubIncrements[nDepth-1].IntervalCount; double* pValue = NULL; for(; pfNextParentTick; fLastParentTick=*pfNextParentTick, pfNextParentTick = aIter.nextValue()) { for( sal_Int32 nPartTick = 1; nPartTickgetMinorTick( nPartTick, nDepth , fLastParentTick, *pfNextParentTick ); if(!pValue) continue; aSubTicks[nRealSubTickCount] = *pValue; nRealSubTickCount++; } } aSubTicks.realloc(nRealSubTickCount); rParentTicks[nDepth] = aSubTicks; if(static_cast(m_rIncrement.SubIncrements.size())>nDepth) addSubTicks( nDepth+1, rParentTicks ); } sal_Int32 EquidistantTickFactory::getMaxTickCount( sal_Int32 nDepth ) const { //return the maximum amount of ticks //possibly open intervals at the two ends of the region are handled as if they were completely visible //(this is necessary for calculating the sub ticks at the borders correctly) if( nDepth >= getTickDepth() ) return 0; if( m_fOuterMajorTickBorderMax < m_fOuterMajorTickBorderMin ) return 0; if( m_rIncrement.Distance<=0.0) return 0; double fSub; if(m_rIncrement.PostEquidistant ) fSub = approxSub( m_fScaledVisibleMax, m_fScaledVisibleMin ); else fSub = approxSub( m_rScale.Maximum, m_rScale.Minimum ); if (!isFinite(fSub)) return 0; double fIntervalCount = fSub / m_rIncrement.Distance; if (fIntervalCount > std::numeric_limits::max()) // Interval count too high! Bail out. return 0; sal_Int32 nIntervalCount = static_cast(fIntervalCount); nIntervalCount+=3; for(sal_Int32 nN=0; nN1 ) nIntervalCount *= m_rIncrement.SubIncrements[nN].IntervalCount; } sal_Int32 nTickCount = nIntervalCount; if(nDepth>0 && m_rIncrement.SubIncrements[nDepth-1].IntervalCount>1) nTickCount = nIntervalCount * (m_rIncrement.SubIncrements[nDepth-1].IntervalCount-1); return nTickCount; } double* EquidistantTickFactory::getMajorTick( sal_Int32 nTick ) const { m_pfCurrentValues[0] = m_fOuterMajorTickBorderMin + nTick*m_rIncrement.Distance; if(m_pfCurrentValues[0]>m_fOuterMajorTickBorderMax) { if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMax) ) return NULL; } if(m_pfCurrentValues[0]doScaling( m_pfCurrentValues[0] ); return &m_pfCurrentValues[0]; } double* EquidistantTickFactory::getMinorTick( sal_Int32 nTick, sal_Int32 nDepth , double fStartParentTick, double fNextParentTick ) const { //check validity of arguments { //OSL_ENSURE( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick"); if(fStartParentTick >= fNextParentTick) return NULL; if(nDepth>static_cast(m_rIncrement.SubIncrements.size()) || nDepth<=0) return NULL; //subticks are only calculated if they are laying between parent ticks: if(nTick<=0) return NULL; if(nTick>=m_rIncrement.SubIncrements[nDepth-1].IntervalCount) return NULL; } bool bPostEquidistant = m_rIncrement.SubIncrements[nDepth-1].PostEquidistant; double fAdaptedStartParent = fStartParentTick; double fAdaptedNextParent = fNextParentTick; if( !bPostEquidistant && m_xInverseScaling.is() ) { fAdaptedStartParent = m_xInverseScaling->doScaling(fStartParentTick); fAdaptedNextParent = m_xInverseScaling->doScaling(fNextParentTick); } double fDistance = (fAdaptedNextParent - fAdaptedStartParent)/m_rIncrement.SubIncrements[nDepth-1].IntervalCount; m_pfCurrentValues[nDepth] = fAdaptedStartParent + nTick*fDistance; //return always the value after scaling if(!bPostEquidistant && m_xInverseScaling.is() ) m_pfCurrentValues[nDepth] = m_rScale.Scaling->doScaling( m_pfCurrentValues[nDepth] ); if( !isWithinOuterBorder( m_pfCurrentValues[nDepth] ) ) return NULL; return &m_pfCurrentValues[nDepth]; } bool EquidistantTickFactory::isWithinOuterBorder( double fScaledValue ) const { if(fScaledValue>m_fOuterMajorTickBorderMax_Scaled) return false; if(fScaledValuem_fScaledVisibleMax) { if( !approxEqual(fScaledValue,m_fScaledVisibleMax) ) return false; } if(fScaledValue >& rAllTickInfos ) const { uno::Sequence< uno::Sequence< double > > aAllTicks; //create point sequences for each tick depth sal_Int32 nDepthCount = this->getTickDepth(); sal_Int32 nMaxMajorTickCount = this->getMaxTickCount( 0 ); if (nDepthCount <= 0 || nMaxMajorTickCount <= 0) return; aAllTicks.realloc(nDepthCount); aAllTicks[0].realloc(nMaxMajorTickCount); sal_Int32 nRealMajorTickCount = 0; double* pValue = NULL; for( sal_Int32 nMajorTick=0; nMajorTickgetMajorTick( nMajorTick ); if(!pValue) continue; aAllTicks[0][nRealMajorTickCount] = *pValue; nRealMajorTickCount++; } if(!nRealMajorTickCount) return; aAllTicks[0].realloc(nRealMajorTickCount); if(nDepthCount>0) this->addSubTicks( 1, aAllTicks ); //so far we have added all ticks between the outer major tick marks //this was necessary to create sub ticks correctly //now we reduce all ticks to the visible ones that lie between the real borders sal_Int32 nDepth = 0; sal_Int32 nTick = 0; for( nDepth = 0; nDepth < nDepthCount; nDepth++) { sal_Int32 nInvisibleAtLowerBorder = 0; sal_Int32 nInvisibleAtUpperBorder = 0; //we need only to check all ticks within the first major interval at each border sal_Int32 nCheckCount = 1; for(sal_Int32 nN=0; nN1 ) nCheckCount *= m_rIncrement.SubIncrements[nN].IntervalCount; } uno::Sequence< double >& rTicks = aAllTicks[nDepth]; sal_Int32 nCount = rTicks.getLength(); //check lower border for( nTick=0; nTicknCount-1-nCheckCount && nTick>=0; nTick--) { if( !isVisible( rTicks[nTick] ) ) nInvisibleAtUpperBorder++; } //resize sequence if( !nInvisibleAtLowerBorder && !nInvisibleAtUpperBorder) continue; if( !nInvisibleAtLowerBorder ) rTicks.realloc(nCount-nInvisibleAtUpperBorder); else { sal_Int32 nNewCount = nCount-nInvisibleAtUpperBorder-nInvisibleAtLowerBorder; if(nNewCount<0) nNewCount=0; uno::Sequence< double > aOldTicks(rTicks); rTicks.realloc(nNewCount); for(nTick = 0; nTick& rTickInfoVector = rAllTickInfos[nDepth]; rTickInfoVector.clear(); rTickInfoVector.reserve( nCount ); for(sal_Int32 nN = 0; nN >& rAllTickInfos ) const { ExplicitIncrementData aShiftedIncrement( m_rIncrement ); aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0; EquidistantTickFactory( m_rScale, aShiftedIncrement ).getAllTicks(rAllTickInfos); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- EquidistantTickIter::EquidistantTickIter( const uno::Sequence< uno::Sequence< double > >& rTicks , const ExplicitIncrementData& rIncrement , sal_Int32 nMinDepth, sal_Int32 nMaxDepth ) : m_pSimpleTicks(&rTicks) , m_pInfoTicks(0) , m_rIncrement(rIncrement) , m_nMinDepth(0), m_nMaxDepth(0) , m_nTickCount(0), m_pnPositions(NULL) , m_pnPreParentCount(NULL), m_pbIntervalFinished(NULL) , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 ) { initIter( nMinDepth, nMaxDepth ); } EquidistantTickIter::EquidistantTickIter( ::std::vector< ::std::vector< TickInfo > >& rTicks , const ExplicitIncrementData& rIncrement , sal_Int32 nMinDepth, sal_Int32 nMaxDepth ) : m_pSimpleTicks(NULL) , m_pInfoTicks(&rTicks) , m_rIncrement(rIncrement) , m_nMinDepth(0), m_nMaxDepth(0) , m_nTickCount(0), m_pnPositions(NULL) , m_pnPreParentCount(NULL), m_pbIntervalFinished(NULL) , m_nCurrentDepth(-1), m_nCurrentPos(-1), m_fCurrentValue( 0.0 ) { initIter( nMinDepth, nMaxDepth ); } void EquidistantTickIter::initIter( sal_Int32 /*nMinDepth*/, sal_Int32 nMaxDepth ) { m_nMaxDepth = nMaxDepth; if(nMaxDepth<0 || m_nMaxDepth>getMaxDepth()) m_nMaxDepth=getMaxDepth(); sal_Int32 nDepth = 0; for( nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ ) m_nTickCount += getTickCount(nDepth); if(!m_nTickCount) return; m_pnPositions = new sal_Int32[m_nMaxDepth+1]; m_pnPreParentCount = new sal_Int32[m_nMaxDepth+1]; m_pbIntervalFinished = new bool[m_nMaxDepth+1]; m_pnPreParentCount[0] = 0; m_pbIntervalFinished[0] = false; double fParentValue = getTickValue(0,0); for( nDepth = 1; nDepth<=m_nMaxDepth ;nDepth++ ) { m_pbIntervalFinished[nDepth] = false; sal_Int32 nPreParentCount = 0; sal_Int32 nCount = getTickCount(nDepth); for(sal_Int32 nN = 0; nNstatic_cast(m_rIncrement.SubIncrements.size()) || nDepth<0) return 0; if(!nDepth) return m_nTickCount; return m_rIncrement.SubIncrements[nDepth-1].IntervalCount; } bool EquidistantTickIter::isAtLastPartTick() { if(!m_nCurrentDepth) return false; sal_Int32 nIntervalCount = getIntervalCount( m_nCurrentDepth ); if(!nIntervalCount || nIntervalCount == 1) return true; if( m_pbIntervalFinished[m_nCurrentDepth] ) return false; sal_Int32 nPos = m_pnPositions[m_nCurrentDepth]+1; if(m_pnPreParentCount[m_nCurrentDepth]) nPos += nIntervalCount-1 - m_pnPreParentCount[m_nCurrentDepth]; bool bRet = nPos && nPos % (nIntervalCount-1) == 0; if(!nPos && !m_pnPreParentCount[m_nCurrentDepth] && m_pnPositions[m_nCurrentDepth-1]==-1 ) bRet = true; return bRet; } bool EquidistantTickIter::gotoFirst() { if( m_nMaxDepth<0 ) return false; if( !m_nTickCount ) return false; for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ ) m_pnPositions[nDepth] = -1; m_nCurrentPos = 0; m_nCurrentDepth = getStartDepth(); m_pnPositions[m_nCurrentDepth] = 0; return true; } bool EquidistantTickIter::gotoNext() { if( m_nCurrentPos < 0 ) return false; m_nCurrentPos++; if( m_nCurrentPos >= m_nTickCount ) return false; if( m_nCurrentDepth==m_nMaxDepth && isAtLastPartTick() ) { do { m_pbIntervalFinished[m_nCurrentDepth] = true; m_nCurrentDepth--; } while( m_nCurrentDepth && isAtLastPartTick() ); } else if( m_nCurrentDepth= m_nTickCount ) return false; if( nTickIndex < m_nCurrentPos ) if( !gotoFirst() ) return false; while( nTickIndex > m_nCurrentPos ) if( !gotoNext() ) return false; return true; } sal_Int32 EquidistantTickIter::getCurrentIndex() const { return m_nCurrentPos; } sal_Int32 EquidistantTickIter::getMaxIndex() const { return m_nTickCount-1; } double* EquidistantTickIter::nextValue() { if( gotoNext() ) { m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]); return &m_fCurrentValue; } return NULL; } TickInfo* EquidistantTickIter::nextInfo() { if( m_pInfoTicks && gotoNext() && static_cast< sal_Int32 >( (*m_pInfoTicks)[m_nCurrentDepth].size()) > m_pnPositions[m_nCurrentDepth] ) { return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]]; } return NULL; } //............................................................................. } //namespace chart //.............................................................................