diff options
Diffstat (limited to 'chart2/source/view/axes')
29 files changed, 7942 insertions, 0 deletions
diff --git a/chart2/source/view/axes/MinimumAndMaximumSupplier.cxx b/chart2/source/view/axes/MinimumAndMaximumSupplier.cxx new file mode 100644 index 000000000000..88f99b493144 --- /dev/null +++ b/chart2/source/view/axes/MinimumAndMaximumSupplier.cxx @@ -0,0 +1,201 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "MinimumAndMaximumSupplier.hxx" +#include <rtl/math.hxx> +#include <com/sun/star/awt/Size.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; + +MergedMinimumAndMaximumSupplier::MergedMinimumAndMaximumSupplier() +{ +} + +MergedMinimumAndMaximumSupplier::~MergedMinimumAndMaximumSupplier() +{ +} + +void MergedMinimumAndMaximumSupplier::addMinimumAndMaximumSupplier( MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier ) +{ + m_aMinimumAndMaximumSupplierList.insert( pMinimumAndMaximumSupplier ); +} + +bool MergedMinimumAndMaximumSupplier::hasMinimumAndMaximumSupplier( MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier ) +{ + return m_aMinimumAndMaximumSupplierList.count( pMinimumAndMaximumSupplier ) != 0; +} + +double MergedMinimumAndMaximumSupplier::getMinimumX() +{ + double fGlobalExtremum; + ::rtl::math::setInf(&fGlobalExtremum, false); + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + { + double fLocalExtremum = (*aIt)->getMinimumX(); + if(fLocalExtremum<fGlobalExtremum) + fGlobalExtremum=fLocalExtremum; + } + if(::rtl::math::isInf(fGlobalExtremum)) + ::rtl::math::setNan(&fGlobalExtremum); + return fGlobalExtremum; +} + +double MergedMinimumAndMaximumSupplier::getMaximumX() +{ + double fGlobalExtremum; + ::rtl::math::setInf(&fGlobalExtremum, true); + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + { + double fLocalExtremum = (*aIt)->getMaximumX(); + if(fLocalExtremum>fGlobalExtremum) + fGlobalExtremum=fLocalExtremum; + } + if(::rtl::math::isInf(fGlobalExtremum)) + ::rtl::math::setNan(&fGlobalExtremum); + return fGlobalExtremum; +} + +double MergedMinimumAndMaximumSupplier::getMinimumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex ) +{ + double fGlobalExtremum; + ::rtl::math::setInf(&fGlobalExtremum, false); + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + { + double fLocalExtremum = (*aIt)->getMinimumYInRange( fMinimumX, fMaximumX, nAxisIndex ); + if(fLocalExtremum<fGlobalExtremum) + fGlobalExtremum=fLocalExtremum; + } + if(::rtl::math::isInf(fGlobalExtremum)) + ::rtl::math::setNan(&fGlobalExtremum); + return fGlobalExtremum; +} + +double MergedMinimumAndMaximumSupplier::getMaximumYInRange( double fMinimumX, double fMaximumX, sal_Int32 nAxisIndex ) +{ + double fGlobalExtremum; + ::rtl::math::setInf(&fGlobalExtremum, true); + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + { + double fLocalExtremum = (*aIt)->getMaximumYInRange( fMinimumX, fMaximumX, nAxisIndex ); + if(fLocalExtremum>fGlobalExtremum) + fGlobalExtremum=fLocalExtremum; + } + if(::rtl::math::isInf(fGlobalExtremum)) + ::rtl::math::setNan(&fGlobalExtremum); + return fGlobalExtremum; +} + +double MergedMinimumAndMaximumSupplier::getMinimumZ() +{ + double fGlobalExtremum; + ::rtl::math::setInf(&fGlobalExtremum, false); + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + { + double fLocalExtremum = (*aIt)->getMinimumZ(); + if(fLocalExtremum<fGlobalExtremum) + fGlobalExtremum=fLocalExtremum; + } + if(::rtl::math::isInf(fGlobalExtremum)) + ::rtl::math::setNan(&fGlobalExtremum); + return fGlobalExtremum; +} + +double MergedMinimumAndMaximumSupplier::getMaximumZ() +{ + double fGlobalExtremum; + ::rtl::math::setInf(&fGlobalExtremum, true); + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + { + double fLocalExtremum = (*aIt)->getMaximumZ(); + if(fLocalExtremum>fGlobalExtremum) + fGlobalExtremum=fLocalExtremum; + } + if(::rtl::math::isInf(fGlobalExtremum)) + ::rtl::math::setNan(&fGlobalExtremum); + return fGlobalExtremum; +} + +bool MergedMinimumAndMaximumSupplier::isExpandBorderToIncrementRhythm( sal_Int32 nDimensionIndex ) +{ + // only return true, if *all* suppliers want to scale to the main tick marks + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + if( !(*aIt)->isExpandBorderToIncrementRhythm( nDimensionIndex ) ) + return false; + return true; +} + +bool MergedMinimumAndMaximumSupplier::isExpandIfValuesCloseToBorder( sal_Int32 nDimensionIndex ) +{ + // only return true, if *all* suppliers want to expand the range + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + if( !(*aIt)->isExpandIfValuesCloseToBorder( nDimensionIndex ) ) + return false; + return true; +} + +bool MergedMinimumAndMaximumSupplier::isExpandWideValuesToZero( sal_Int32 nDimensionIndex ) +{ + // already return true, if at least one supplier wants to expand the range + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + if( (*aIt)->isExpandWideValuesToZero( nDimensionIndex ) ) + return true; + return false; +} + +bool MergedMinimumAndMaximumSupplier::isExpandNarrowValuesTowardZero( sal_Int32 nDimensionIndex ) +{ + // already return true, if at least one supplier wants to expand the range + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + if( (*aIt)->isExpandNarrowValuesTowardZero( nDimensionIndex ) ) + return true; + return false; +} + +bool MergedMinimumAndMaximumSupplier::isSeperateStackingForDifferentSigns( sal_Int32 nDimensionIndex ) +{ + // should not be called + for( MinimumAndMaximumSupplierSet::iterator aIt = begin(), aEnd = end(); aIt != aEnd; ++aIt ) + if( (*aIt)->isSeperateStackingForDifferentSigns( nDimensionIndex ) ) + return true; + return false; +} + +void MergedMinimumAndMaximumSupplier::clearMinimumAndMaximumSupplierList() +{ + m_aMinimumAndMaximumSupplierList.clear(); +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/ScaleAutomatism.cxx b/chart2/source/view/axes/ScaleAutomatism.cxx new file mode 100644 index 000000000000..890bf87df96e --- /dev/null +++ b/chart2/source/view/axes/ScaleAutomatism.cxx @@ -0,0 +1,760 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "ScaleAutomatism.hxx" +#include "macros.hxx" +#include "TickmarkHelper.hxx" +#include "AxisHelper.hxx" +#include <rtl/math.hxx> +#include <tools/debug.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +const sal_Int32 MAXIMUM_MANUAL_INCREMENT_COUNT = 500; +const sal_Int32 MAXIMUM_AUTO_INCREMENT_COUNT = 10; +const sal_Int32 MAXIMUM_SUB_INCREMENT_COUNT = 100; + +namespace +{ + +void lcl_ensureMaximumSubIncrementCount( sal_Int32& rnSubIntervalCount ) +{ + if( rnSubIntervalCount > MAXIMUM_SUB_INCREMENT_COUNT ) + rnSubIntervalCount = MAXIMUM_SUB_INCREMENT_COUNT; +} + +}//end anonymous namespace + +ScaleAutomatism::ScaleAutomatism( const ScaleData& rSourceScale ) + : m_aSourceScale( rSourceScale ) + , m_fValueMinimum( 0.0 ) + , m_fValueMaximum( 0.0 ) + , m_nMaximumAutoMainIncrementCount( MAXIMUM_AUTO_INCREMENT_COUNT ) + , m_bExpandBorderToIncrementRhythm( false ) + , m_bExpandIfValuesCloseToBorder( false ) + , m_bExpandWideValuesToZero( false ) + , m_bExpandNarrowValuesTowardZero( false ) +{ + ::rtl::math::setNan( &m_fValueMinimum ); + ::rtl::math::setNan( &m_fValueMaximum ); + + double fExplicitOrigin = 0.0; + if( m_aSourceScale.Origin >>= fExplicitOrigin ) + expandValueRange( fExplicitOrigin, fExplicitOrigin); +} +ScaleAutomatism::~ScaleAutomatism() +{ +} + +void ScaleAutomatism::expandValueRange( double fMinimum, double fMaximum ) +{ + if( (fMinimum < m_fValueMinimum) || ::rtl::math::isNan( m_fValueMinimum ) ) + m_fValueMinimum = fMinimum; + if( (fMaximum > m_fValueMaximum) || ::rtl::math::isNan( m_fValueMaximum ) ) + m_fValueMaximum = fMaximum; +} + +void ScaleAutomatism::setAutoScalingOptions( + bool bExpandBorderToIncrementRhythm, + bool bExpandIfValuesCloseToBorder, + bool bExpandWideValuesToZero, + bool bExpandNarrowValuesTowardZero ) +{ + // if called multiple times, enable an option, if it is set in at least one call + m_bExpandBorderToIncrementRhythm |= bExpandBorderToIncrementRhythm; + m_bExpandIfValuesCloseToBorder |= bExpandIfValuesCloseToBorder; + m_bExpandWideValuesToZero |= bExpandWideValuesToZero; + m_bExpandNarrowValuesTowardZero |= bExpandNarrowValuesTowardZero; + + if( m_aSourceScale.AxisType==AxisType::PERCENT ) + m_bExpandIfValuesCloseToBorder = false; +} + +void ScaleAutomatism::setMaximumAutoMainIncrementCount( sal_Int32 nMaximumAutoMainIncrementCount ) +{ + if( nMaximumAutoMainIncrementCount < 2 ) + m_nMaximumAutoMainIncrementCount = 2; //#i82006 + else if( nMaximumAutoMainIncrementCount > MAXIMUM_AUTO_INCREMENT_COUNT ) + m_nMaximumAutoMainIncrementCount = MAXIMUM_AUTO_INCREMENT_COUNT; + else + m_nMaximumAutoMainIncrementCount = nMaximumAutoMainIncrementCount; +} + +void ScaleAutomatism::calculateExplicitScaleAndIncrement( + ExplicitScaleData& rExplicitScale, ExplicitIncrementData& rExplicitIncrement ) const +{ + // fill explicit scale + rExplicitScale.Orientation = m_aSourceScale.Orientation; + rExplicitScale.Scaling = m_aSourceScale.Scaling; + rExplicitScale.Breaks = m_aSourceScale.Breaks; + rExplicitScale.AxisType = m_aSourceScale.AxisType; + + bool bAutoMinimum = !(m_aSourceScale.Minimum >>= rExplicitScale.Minimum); + bool bAutoMaximum = !(m_aSourceScale.Maximum >>= rExplicitScale.Maximum); + bool bAutoOrigin = !(m_aSourceScale.Origin >>= rExplicitScale.Origin); + + // automatic scale minimum + if( bAutoMinimum ) + { + if( m_aSourceScale.AxisType==AxisType::PERCENT ) + rExplicitScale.Minimum = 0.0; + else if( ::rtl::math::isNan( m_fValueMinimum ) ) + rExplicitScale.Minimum = 0.0; //@todo get Minimum from scaling or from plotter???? + else + rExplicitScale.Minimum = m_fValueMinimum; + } + + // automatic scale maximum + if( bAutoMaximum ) + { + if( m_aSourceScale.AxisType==AxisType::PERCENT ) + rExplicitScale.Maximum = 1.0; + else if( ::rtl::math::isNan( m_fValueMaximum ) ) + rExplicitScale.Maximum = 10.0; //@todo get Maximum from scaling or from plotter???? + else + rExplicitScale.Maximum = m_fValueMaximum; + } + + //--------------------------------------------------------------- + //fill explicit increment + + rExplicitIncrement.ShiftedPosition = (m_aSourceScale.AxisType==AxisType::SERIES) ? true : false; + bool bIsLogarithm = false; + + //minimum and maximum of the ExplicitScaleData may be changed if allowed + if( m_aSourceScale.AxisType==AxisType::CATEGORY || m_aSourceScale.AxisType==AxisType::SERIES ) + { + calculateExplicitIncrementAndScaleForCategory( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum ); + } + else + { + bIsLogarithm = AxisHelper::isLogarithmic( rExplicitScale.Scaling ); + if( bIsLogarithm ) + calculateExplicitIncrementAndScaleForLogarithmic( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum ); + else + calculateExplicitIncrementAndScaleForLinear( rExplicitScale, rExplicitIncrement, bAutoMinimum, bAutoMaximum ); + } + + // automatic origin + if( bAutoOrigin ) + { + // #i71415# automatic origin for logarithmic axis + double fDefaulOrigin = bIsLogarithm ? 1.0 : 0.0; + + if( fDefaulOrigin < rExplicitScale.Minimum ) + fDefaulOrigin = rExplicitScale.Minimum; + else if( fDefaulOrigin > rExplicitScale.Maximum ) + fDefaulOrigin = rExplicitScale.Maximum; + + rExplicitScale.Origin = fDefaulOrigin; + } +} + +ScaleData ScaleAutomatism::getScale() const +{ + return m_aSourceScale; +} + +// private -------------------------------------------------------------------- + +void ScaleAutomatism::calculateExplicitIncrementAndScaleForCategory( + ExplicitScaleData& rExplicitScale, + ExplicitIncrementData& rExplicitIncrement, + bool bAutoMinimum, bool bAutoMaximum ) const +{ + // no scaling for categories + rExplicitScale.Scaling.clear(); + + // ensure that at least one category is visible + if( rExplicitScale.Maximum <= rExplicitScale.Minimum ) + rExplicitScale.Maximum = rExplicitScale.Minimum + 1.0; + + // default increment settings + rExplicitIncrement.PostEquidistant = sal_True; // does not matter anyhow + rExplicitIncrement.Distance = 1.0; // category axis always have a main increment of 1 + rExplicitIncrement.BaseValue = 0.0; // category axis always have a base of 0 + + // automatic minimum and maximum + if( bAutoMinimum && m_bExpandBorderToIncrementRhythm ) + rExplicitScale.Minimum = TickmarkHelper::getMinimumAtIncrement( rExplicitScale.Minimum, rExplicitIncrement ); + if( bAutoMaximum && m_bExpandBorderToIncrementRhythm ) + rExplicitScale.Maximum = TickmarkHelper::getMaximumAtIncrement( rExplicitScale.Maximum, rExplicitIncrement ); + + //prevent performace killover + double fDistanceCount = ::rtl::math::approxFloor( (rExplicitScale.Maximum-rExplicitScale.Minimum) / rExplicitIncrement.Distance ); + if( static_cast< sal_Int32 >( fDistanceCount ) > MAXIMUM_MANUAL_INCREMENT_COUNT ) + { + double fMinimumFloor = ::rtl::math::approxFloor( rExplicitScale.Minimum ); + double fMaximumCeil = ::rtl::math::approxCeil( rExplicitScale.Maximum ); + rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / MAXIMUM_MANUAL_INCREMENT_COUNT ); + } + + //--------------------------------------------------------------- + //fill explicit sub increment + sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength(); + rExplicitIncrement.SubIncrements.realloc(nSubCount); + for( sal_Int32 nN=0; nN<nSubCount; nN++ ) + { + const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN]; + ExplicitSubIncrement& rExplicitSubIncrement = rExplicitIncrement.SubIncrements[nN]; + + if(!(rSubIncrement.IntervalCount>>=rExplicitSubIncrement.IntervalCount)) + { + //scaling dependent + //@todo autocalculate IntervalCount dependent on MainIncrement and scaling + rExplicitSubIncrement.IntervalCount = 2; + } + lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement.IntervalCount ); + if(!(rSubIncrement.PostEquidistant>>=rExplicitSubIncrement.PostEquidistant)) + { + //scaling dependent + rExplicitSubIncrement.PostEquidistant = sal_False; + } + } +} + +//@todo these method should become part of the scaling interface and implementation somehow +//@todo problem with outparamters at api +void ScaleAutomatism::calculateExplicitIncrementAndScaleForLogarithmic( + ExplicitScaleData& rExplicitScale, + ExplicitIncrementData& rExplicitIncrement, + bool bAutoMinimum, bool bAutoMaximum ) const +{ + // *** STEP 1: initialize the range data *** + + const double fInputMinimum = rExplicitScale.Minimum; + const double fInputMaximum = rExplicitScale.Maximum; + + double fSourceMinimum = rExplicitScale.Minimum; + double fSourceMaximum = rExplicitScale.Maximum; + + // set automatic PostEquidistant to true (maybe scaling dependent?) + // Note: scaling with PostEquidistant==false is untested and needs review + if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) ) + rExplicitIncrement.PostEquidistant = sal_True; + + /* All following scaling code will operate on the logarithms of the source + values. In the last step, the original values will be restored. */ + uno::Reference< XScaling > xScaling = rExplicitScale.Scaling; + if( !xScaling.is() ) + xScaling.set( AxisHelper::createLogarithmicScaling() ); + uno::Reference< XScaling > xInverseScaling = xScaling->getInverseScaling(); + + fSourceMinimum = xScaling->doScaling( fSourceMinimum ); + if( !::rtl::math::isFinite( fSourceMinimum ) ) + fSourceMinimum = 0.0; + else if( ::rtl::math::approxEqual( fSourceMinimum, ::rtl::math::approxFloor( fSourceMinimum ) ) ) + fSourceMinimum = ::rtl::math::approxFloor( fSourceMinimum ); + + fSourceMaximum = xScaling->doScaling( fSourceMaximum ); + if( !::rtl::math::isFinite( fSourceMaximum ) ) + fSourceMaximum = 0.0; + else if( ::rtl::math::approxEqual( fSourceMaximum, ::rtl::math::approxFloor( fSourceMaximum ) ) ) + fSourceMaximum = ::rtl::math::approxFloor( fSourceMaximum ); + + /* If range is invalid (minimum greater than maximum), change one of the + variable limits to validate the range. In this step, a zero-sized range + is still allowed. */ + if( fSourceMinimum > fSourceMaximum ) + { + // force changing the maximum, if both limits are fixed + if( bAutoMaximum || !bAutoMinimum ) + fSourceMaximum = fSourceMinimum; + else + fSourceMinimum = fSourceMaximum; + } + + /* If maximum is less than 0 (and therefore minimum too), minimum and + maximum will be negated and swapped to make the following algorithms + easier. Example: Both ranges [2,5] and [-5,-2] will be processed as + [2,5], and the latter will be swapped back later. The range [0,0] is + explicitly excluded from swapping (this would result in [-1,0] instead + of the expected [0,1]). */ + bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0); + if( bSwapAndNegateRange ) + { + double fTempValue = fSourceMinimum; + fSourceMinimum = -fSourceMaximum; + fSourceMaximum = -fTempValue; + ::std::swap( bAutoMinimum, bAutoMaximum ); + } + + // *** STEP 2: find temporary (unrounded) axis minimum and maximum *** + + double fTempMinimum = fSourceMinimum; + double fTempMaximum = fSourceMaximum; + + /* If minimum is variable and greater than 0 (and therefore maximum too), + means all original values are greater than 1 (or all values are less + than 1, and the range has been swapped above), then: */ + if( bAutoMinimum && (fTempMinimum > 0.0) ) + { + /* If minimum is less than 5 (i.e. original source values less than + B^5, B being the base of the scaling), or if minimum and maximum + are in different increment intervals (means, if minimum and maximum + are not both in the range [B^n,B^(n+1)] for a whole number n), set + minimum to 0, which results in B^0=1 on the axis. */ + double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum ); + double fMaximumFloor = ::rtl::math::approxFloor( fTempMaximum ); + // handle the exact value B^(n+1) to be in the range [B^n,B^(n+1)] + if( ::rtl::math::approxEqual( fTempMaximum, fMaximumFloor ) ) + fMaximumFloor -= 1.0; + + if( (fMinimumFloor < 5.0) || (fMinimumFloor < fMaximumFloor) ) + { + if( m_bExpandWideValuesToZero ) + fTempMinimum = 0.0; + } + /* Else (minimum and maximum are in one increment interval), expand + minimum toward 0 to make the 'shorter' data points visible. */ + else + { + if( m_bExpandNarrowValuesTowardZero ) + fTempMinimum -= 1.0; + } + } + + /* If range is still zero-sized (e.g. when minimum is fixed), set minimum + to 0, which makes the axis start/stop at the value 1. */ + if( fTempMinimum == fTempMaximum ) + { + if( bAutoMinimum && (fTempMaximum > 0.0) ) + fTempMinimum = 0.0; + else + fTempMaximum += 1.0; // always add one interval, even if maximum is fixed + } + + // *** STEP 3: calculate main interval size *** + + // base value (anchor position of the intervals), already scaled + if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) ) + { + //scaling dependent + //@maybe todo is this default also plotter dependent ?? + if( !bAutoMinimum ) + rExplicitIncrement.BaseValue = fTempMinimum; + else if( !bAutoMaximum ) + rExplicitIncrement.BaseValue = fTempMaximum; + else + rExplicitIncrement.BaseValue = 0.0; + } + + // calculate automatic interval + bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance); + if( bAutoDistance ) + rExplicitIncrement.Distance = 0.0; + + /* Restrict number of allowed intervals with user-defined distance to + MAXIMUM_MANUAL_INCREMENT_COUNT. */ + sal_Int32 nMaxMainIncrementCount = bAutoDistance ? + m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT; + + // repeat calculation until number of intervals are valid + bool bNeedIteration = true; + bool bHasCalculatedDistance = false; + while( bNeedIteration ) + { + if( bAutoDistance ) + { + // first iteration: calculate interval size from axis limits + if( !bHasCalculatedDistance ) + { + double fMinimumFloor = ::rtl::math::approxFloor( fTempMinimum ); + double fMaximumCeil = ::rtl::math::approxCeil( fTempMaximum ); + rExplicitIncrement.Distance = ::rtl::math::approxCeil( (fMaximumCeil - fMinimumFloor) / nMaxMainIncrementCount ); + } + else + { + // following iterations: increase distance + rExplicitIncrement.Distance += 1.0; + } + + // for next iteration: distance calculated -> use else path to increase + bHasCalculatedDistance = true; + } + + // *** STEP 4: additional space above or below the data points *** + + double fAxisMinimum = fTempMinimum; + double fAxisMaximum = fTempMaximum; + + // round to entire multiples of the distance and add additional space + if( bAutoMinimum && m_bExpandBorderToIncrementRhythm ) + { + fAxisMinimum = TickmarkHelper::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement ); + + //ensure valid values after scaling #i100995# + if( !bAutoDistance ) + { + double fCheck = xInverseScaling->doScaling( fAxisMinimum ); + if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 ) + { + bAutoDistance = true; + bHasCalculatedDistance = false; + continue; + } + } + } + if( bAutoMaximum && m_bExpandBorderToIncrementRhythm ) + { + fAxisMaximum = TickmarkHelper::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement ); + + //ensure valid values after scaling #i100995# + if( !bAutoDistance ) + { + double fCheck = xInverseScaling->doScaling( fAxisMaximum ); + if( !::rtl::math::isFinite( fCheck ) || fCheck <= 0 ) + { + bAutoDistance = true; + bHasCalculatedDistance = false; + continue; + } + } + } + + // set the resulting limits (swap back to negative range if needed) + if( bSwapAndNegateRange ) + { + rExplicitScale.Minimum = -fAxisMaximum; + rExplicitScale.Maximum = -fAxisMinimum; + } + else + { + rExplicitScale.Minimum = fAxisMinimum; + rExplicitScale.Maximum = fAxisMaximum; + } + + /* If the number of intervals is too high (e.g. due to invalid fixed + distance or due to added space above or below data points), + calculate again with increased distance. */ + double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance ); + bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount; + // if manual distance is invalid, trigger automatic calculation + if( bNeedIteration ) + bAutoDistance = true; + + // convert limits back to logarithmic scale + rExplicitScale.Minimum = xInverseScaling->doScaling( rExplicitScale.Minimum ); + rExplicitScale.Maximum = xInverseScaling->doScaling( rExplicitScale.Maximum ); + + //ensure valid values after scaling #i100995# + if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0) + { + rExplicitScale.Minimum = fInputMinimum; + if( !::rtl::math::isFinite( rExplicitScale.Minimum ) || rExplicitScale.Minimum <= 0 ) + rExplicitScale.Minimum = 1.0; + } + if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 ) + { + rExplicitScale.Maximum= fInputMaximum; + if( !::rtl::math::isFinite( rExplicitScale.Maximum) || rExplicitScale.Maximum <= 0 ) + rExplicitScale.Maximum = 10.0; + } + if( rExplicitScale.Maximum < rExplicitScale.Minimum ) + ::std::swap( rExplicitScale.Maximum, rExplicitScale.Minimum ); + } + + //--------------------------------------------------------------- + //fill explicit sub increment + sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength(); + rExplicitIncrement.SubIncrements.realloc(nSubCount); + for( sal_Int32 nN=0; nN<nSubCount; nN++ ) + { + const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN]; + ExplicitSubIncrement& rExplicitSubIncrement = rExplicitIncrement.SubIncrements[nN]; + + if(!(rSubIncrement.IntervalCount>>=rExplicitSubIncrement.IntervalCount)) + { + //scaling dependent + //@todo autocalculate IntervalCount dependent on MainIncrement and scaling + rExplicitSubIncrement.IntervalCount = 9; + } + lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement.IntervalCount ); + if(!(rSubIncrement.PostEquidistant>>=rExplicitSubIncrement.PostEquidistant)) + { + //scaling dependent + rExplicitSubIncrement.PostEquidistant = sal_False; + } + } +} + +void ScaleAutomatism::calculateExplicitIncrementAndScaleForLinear( + ExplicitScaleData& rExplicitScale, + ExplicitIncrementData& rExplicitIncrement, + bool bAutoMinimum, bool bAutoMaximum ) const +{ + // *** STEP 1: initialize the range data *** + + double fSourceMinimum = rExplicitScale.Minimum; + double fSourceMaximum = rExplicitScale.Maximum; + + // set automatic PostEquidistant to true (maybe scaling dependent?) + if( !(m_aSourceScale.IncrementData.PostEquidistant >>= rExplicitIncrement.PostEquidistant) ) + rExplicitIncrement.PostEquidistant = sal_True; + + /* If range is invalid (minimum greater than maximum), change one of the + variable limits to validate the range. In this step, a zero-sized range + is still allowed. */ + if( fSourceMinimum > fSourceMaximum ) + { + // force changing the maximum, if both limits are fixed + if( bAutoMaximum || !bAutoMinimum ) + fSourceMaximum = fSourceMinimum; + else + fSourceMinimum = fSourceMaximum; + } + + /* If maximum is zero or negative (and therefore minimum too), minimum and + maximum will be negated and swapped to make the following algorithms + easier. Example: Both ranges [2,5] and [-5,-2] will be processed as + [2,5], and the latter will be swapped back later. The range [0,0] is + explicitly excluded from swapping (this would result in [-1,0] instead + of the expected [0,1]). */ + bool bSwapAndNegateRange = (fSourceMinimum < 0.0) && (fSourceMaximum <= 0.0); + if( bSwapAndNegateRange ) + { + double fTempValue = fSourceMinimum; + fSourceMinimum = -fSourceMaximum; + fSourceMaximum = -fTempValue; + ::std::swap( bAutoMinimum, bAutoMaximum ); + } + + // *** STEP 2: find temporary (unrounded) axis minimum and maximum *** + + double fTempMinimum = fSourceMinimum; + double fTempMaximum = fSourceMaximum; + + /* If minimum is variable and greater than 0 (and therefore maximum too), + means all values are positive (or all values are negative, and the + range has been swapped above), then: */ + if( bAutoMinimum && (fTempMinimum > 0.0) ) + { + /* If minimum equals maximum, or if minimum is less than 5/6 of + maximum, set minimum to 0. */ + if( (fTempMinimum == fTempMaximum) || (fTempMinimum / fTempMaximum < 5.0 / 6.0) ) + { + if( m_bExpandWideValuesToZero ) + fTempMinimum = 0.0; + } + /* Else (minimum is greater than or equal to 5/6 of maximum), add half + of the visible range (expand minimum toward 0) to make the + 'shorter' data points visible. */ + else + { + if( m_bExpandNarrowValuesTowardZero ) + fTempMinimum -= (fTempMaximum - fTempMinimum) / 2.0; + } + } + + /* If range is still zero-sized (e.g. when minimum is fixed), add some + space to a variable limit. */ + if( fTempMinimum == fTempMaximum ) + { + if( bAutoMaximum || !bAutoMinimum ) + { + // change 0 to 1, otherwise double the value + if( fTempMaximum == 0.0 ) + fTempMaximum = 1.0; + else + fTempMaximum *= 2.0; + } + else + { + // change 0 to -1, otherwise halve the value + if( fTempMinimum == 0.0 ) + fTempMinimum = -1.0; + else + fTempMinimum /= 2.0; + } + } + + // *** STEP 3: calculate main interval size *** + + // base value (anchor position of the intervals) + if( !(m_aSourceScale.IncrementData.BaseValue >>= rExplicitIncrement.BaseValue) ) + { + if( !bAutoMinimum ) + rExplicitIncrement.BaseValue = fTempMinimum; + else if( !bAutoMaximum ) + rExplicitIncrement.BaseValue = fTempMaximum; + else + rExplicitIncrement.BaseValue = 0.0; + } + + // calculate automatic interval + bool bAutoDistance = !(m_aSourceScale.IncrementData.Distance >>= rExplicitIncrement.Distance); + /* Restrict number of allowed intervals with user-defined distance to + MAXIMUM_MANUAL_INCREMENT_COUNT. */ + sal_Int32 nMaxMainIncrementCount = bAutoDistance ? + m_nMaximumAutoMainIncrementCount : MAXIMUM_MANUAL_INCREMENT_COUNT; + + double fDistanceMagnitude = 0.0; + double fDistanceNormalized = 0.0; + bool bHasNormalizedDistance = false; + + // repeat calculation until number of intervals are valid + bool bNeedIteration = true; + while( bNeedIteration ) + { + if( bAutoDistance ) + { + // first iteration: calculate interval size from axis limits + if( !bHasNormalizedDistance ) + { + // raw size of an interval + double fDistance = (fTempMaximum - fTempMinimum) / nMaxMainIncrementCount; + + // if distance of is less than 1e-307, do not do anything + if( fDistance <= 1.0e-307 ) + { + fDistanceNormalized = 1.0; + fDistanceMagnitude = 1.0e-307; + } + else + { + // distance magnitude (a power of 10) + int nExponent = static_cast< int >( ::rtl::math::approxFloor( log10( fDistance ) ) ); + fDistanceMagnitude = ::rtl::math::pow10Exp( 1.0, nExponent ); + + // stick normalized distance to a few predefined values + fDistanceNormalized = fDistance / fDistanceMagnitude; + if( fDistanceNormalized <= 1.0 ) + fDistanceNormalized = 1.0; + else if( fDistanceNormalized <= 2.0 ) + fDistanceNormalized = 2.0; + else if( fDistanceNormalized <= 5.0 ) + fDistanceNormalized = 5.0; + else + { + fDistanceNormalized = 1.0; + fDistanceMagnitude *= 10; + } + } + // for next iteration: distance is normalized -> use else path to increase distance + bHasNormalizedDistance = true; + } + // following iterations: increase distance, use only allowed values + else + { + if( fDistanceNormalized == 1.0 ) + fDistanceNormalized = 2.0; + else if( fDistanceNormalized == 2.0 ) + fDistanceNormalized = 5.0; + else + { + fDistanceNormalized = 1.0; + fDistanceMagnitude *= 10; + } + } + + // set the resulting distance + rExplicitIncrement.Distance = fDistanceNormalized * fDistanceMagnitude; + } + + // *** STEP 4: additional space above or below the data points *** + + double fAxisMinimum = fTempMinimum; + double fAxisMaximum = fTempMaximum; + + // round to entire multiples of the distance and add additional space + if( bAutoMinimum ) + { + // round to entire multiples of the distance, based on the base value + if( m_bExpandBorderToIncrementRhythm ) + fAxisMinimum = TickmarkHelper::getMinimumAtIncrement( fAxisMinimum, rExplicitIncrement ); + // additional space, if source minimum is to near at axis minimum + if( m_bExpandIfValuesCloseToBorder ) + if( (fAxisMinimum != 0.0) && ((fAxisMaximum - fSourceMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) ) + fAxisMinimum -= rExplicitIncrement.Distance; + } + if( bAutoMaximum ) + { + // round to entire multiples of the distance, based on the base value + if( m_bExpandBorderToIncrementRhythm ) + fAxisMaximum = TickmarkHelper::getMaximumAtIncrement( fAxisMaximum, rExplicitIncrement ); + // additional space, if source maximum is to near at axis maximum + if( m_bExpandIfValuesCloseToBorder ) + if( (fAxisMaximum != 0.0) && ((fSourceMaximum - fAxisMinimum) / (fAxisMaximum - fAxisMinimum) > 20.0 / 21.0) ) + fAxisMaximum += rExplicitIncrement.Distance; + } + + // set the resulting limits (swap back to negative range if needed) + if( bSwapAndNegateRange ) + { + rExplicitScale.Minimum = -fAxisMaximum; + rExplicitScale.Maximum = -fAxisMinimum; + } + else + { + rExplicitScale.Minimum = fAxisMinimum; + rExplicitScale.Maximum = fAxisMaximum; + } + + /* If the number of intervals is too high (e.g. due to invalid fixed + distance or due to added space above or below data points), + calculate again with increased distance. */ + double fDistanceCount = ::rtl::math::approxFloor( (fAxisMaximum - fAxisMinimum) / rExplicitIncrement.Distance ); + bNeedIteration = static_cast< sal_Int32 >( fDistanceCount ) > nMaxMainIncrementCount; + // if manual distance is invalid, trigger automatic calculation + if( bNeedIteration ) + bAutoDistance = true; + } + + //--------------------------------------------------------------- + //fill explicit sub increment + sal_Int32 nSubCount = m_aSourceScale.IncrementData.SubIncrements.getLength(); + rExplicitIncrement.SubIncrements.realloc(nSubCount); + for( sal_Int32 nN=0; nN<nSubCount; nN++ ) + { + const SubIncrement& rSubIncrement = m_aSourceScale.IncrementData.SubIncrements[nN]; + ExplicitSubIncrement& rExplicitSubIncrement = rExplicitIncrement.SubIncrements[nN]; + + if(!(rSubIncrement.IntervalCount>>=rExplicitSubIncrement.IntervalCount)) + { + //scaling dependent + //@todo autocalculate IntervalCount dependent on MainIncrement and scaling + rExplicitSubIncrement.IntervalCount = 2; + } + lcl_ensureMaximumSubIncrementCount( rExplicitSubIncrement.IntervalCount ); + if(!(rSubIncrement.PostEquidistant>>=rExplicitSubIncrement.PostEquidistant)) + { + //scaling dependent + rExplicitSubIncrement.PostEquidistant = sal_False; + } + } +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/TickmarkHelper.cxx b/chart2/source/view/axes/TickmarkHelper.cxx new file mode 100644 index 000000000000..9e2e2707c035 --- /dev/null +++ b/chart2/source/view/axes/TickmarkHelper.cxx @@ -0,0 +1,937 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "TickmarkHelper.hxx" +#include "ViewDefines.hxx" +#include <rtl/math.hxx> +#include <tools/debug.hxx> +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using namespace ::rtl::math; +using ::basegfx::B2DVector; + +TickInfo::TickInfo() +: fScaledTickValue( 0.0 ) +, fUnscaledTickValue( 0.0 ) +, aTickScreenPosition(0.0,0.0) +, bPaintIt( true ) +, xTextShape( NULL ) +, nFactorForLimitedTextWidth(1) +{ +} + +void TickInfo::updateUnscaledValue( const uno::Reference< XScaling >& xInverseScaling ) +{ + if( xInverseScaling.is() ) + this->fUnscaledTickValue = xInverseScaling->doScaling( this->fScaledTickValue ); + else + this->fUnscaledTickValue = this->fScaledTickValue; +} + +sal_Int32 TickInfo::getScreenDistanceBetweenTicks( const TickInfo& rOherTickInfo ) const +{ + //return the positive distance between the two first tickmarks in screen values + + B2DVector aDistance = rOherTickInfo.aTickScreenPosition - aTickScreenPosition; + sal_Int32 nRet = static_cast<sal_Int32>(aDistance.getLength()); + if(nRet<0) + nRet *= -1; + return nRet; +} + +PureTickIter::PureTickIter( ::std::vector< TickInfo >& rTickInfoVector ) + : m_rTickVector(rTickInfoVector) + , m_aTickIter(m_rTickVector.begin()) +{ +} +PureTickIter::~PureTickIter() +{ +} +TickInfo* PureTickIter::firstInfo() +{ + m_aTickIter = m_rTickVector.begin(); + if(m_aTickIter!=m_rTickVector.end()) + return &*m_aTickIter; + return 0; +} +TickInfo* PureTickIter::nextInfo() +{ + m_aTickIter++; + if(m_aTickIter!=m_rTickVector.end()) + return &*m_aTickIter; + return 0; +} + +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; nN<nCount; nN++) + { + if(getTickValue(nDepth,nN) < fParentValue) + nPreParentCount++; + else + break; + } + m_pnPreParentCount[nDepth] = nPreParentCount; + if(nCount) + { + double fNextParentValue = getTickValue(nDepth,0); + if( fNextParentValue < fParentValue ) + fParentValue = fNextParentValue; + } + } +} + +EquidistantTickIter::~EquidistantTickIter() +{ + delete[] m_pnPositions; + delete[] m_pnPreParentCount; + delete[] m_pbIntervalFinished; +} + +sal_Int32 EquidistantTickIter::getStartDepth() const +{ + //find the depth of the first visible tickmark: + //it is the depth of the smallest value + sal_Int32 nReturnDepth=0; + double fMinValue = DBL_MAX; + for(sal_Int32 nDepth = 0; nDepth<=m_nMaxDepth ;nDepth++ ) + { + sal_Int32 nCount = getTickCount(nDepth); + if( !nCount ) + continue; + double fThisValue = getTickValue(nDepth,0); + if(fThisValue<fMinValue) + { + nReturnDepth = nDepth; + fMinValue = fThisValue; + } + } + return nReturnDepth; +} + +double* EquidistantTickIter::firstValue() +{ + if( gotoFirst() ) + { + m_fCurrentValue = getTickValue(m_nCurrentDepth, m_pnPositions[m_nCurrentDepth]); + return &m_fCurrentValue; + } + return NULL; +} + +TickInfo* EquidistantTickIter::firstInfo() +{ + if( m_pInfoTicks && gotoFirst() ) + return &(*m_pInfoTicks)[m_nCurrentDepth][m_pnPositions[m_nCurrentDepth]]; + return NULL; +} + +sal_Int32 EquidistantTickIter::getIntervalCount( sal_Int32 nDepth ) +{ + if(nDepth>m_rIncrement.SubIncrements.getLength() || 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_nMaxDepth ) + { + do + { + m_nCurrentDepth++; + } + while( m_nCurrentDepth<m_nMaxDepth ); + } + m_pbIntervalFinished[m_nCurrentDepth] = false; + m_pnPositions[m_nCurrentDepth] = m_pnPositions[m_nCurrentDepth]+1; + return true; +} + +bool EquidistantTickIter::gotoIndex( sal_Int32 nTickIndex ) +{ + if( nTickIndex < 0 ) + return false; + if( nTickIndex >= 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; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +//static +double TickmarkHelper::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 TickmarkHelper::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; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +TickmarkHelper::TickmarkHelper( + 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(); + DBG_ASSERT( 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 = TickmarkHelper::getMinimumAtIncrement( fMin, m_rIncrement ); + m_fOuterMajorTickBorderMax = TickmarkHelper::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); + } + } +} + +TickmarkHelper* TickmarkHelper::createShiftedTickmarkHelper() const +{ + ExplicitIncrementData aShiftedIncrement( m_rIncrement ); + aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0; + return new TickmarkHelper( m_rScale, aShiftedIncrement ); +} + +TickmarkHelper::~TickmarkHelper() +{ + delete[] m_pfCurrentValues; +} + +sal_Int32 TickmarkHelper::getTickDepth() const +{ + return m_rIncrement.SubIncrements.getLength() + 1; +} + +sal_Int32 TickmarkHelper::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; + + sal_Int32 nIntervalCount = static_cast<sal_Int32>( fSub / m_rIncrement.Distance ); + + nIntervalCount+=3; + for(sal_Int32 nN=0; nN<nDepth-1; nN++) + { + if( m_rIncrement.SubIncrements[nN].IntervalCount>1 ) + 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* TickmarkHelper::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]<m_fOuterMajorTickBorderMin) + { + if( !approxEqual(m_pfCurrentValues[0],m_fOuterMajorTickBorderMin) ) + return NULL; + } + + //return always the value after scaling + if(!m_rIncrement.PostEquidistant && m_xInverseScaling.is() ) + m_pfCurrentValues[0] = m_rScale.Scaling->doScaling( m_pfCurrentValues[0] ); + + return &m_pfCurrentValues[0]; +} + +double* TickmarkHelper::getMinorTick( sal_Int32 nTick, sal_Int32 nDepth + , double fStartParentTick, double fNextParentTick ) const +{ + //check validity of arguments + { + //DBG_ASSERT( fStartParentTick < fNextParentTick, "fStartParentTick >= fNextParentTick"); + if(fStartParentTick >= fNextParentTick) + return NULL; + if(nDepth>m_rIncrement.SubIncrements.getLength() || 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 TickmarkHelper::isWithinOuterBorder( double fScaledValue ) const +{ + if(fScaledValue>m_fOuterMajorTickBorderMax_Scaled) + return false; + if(fScaledValue<m_fOuterMajorTickBorderMin_Scaled) + return false; + + return true; +} + + +bool TickmarkHelper::isVisible( double fScaledValue ) const +{ + if(fScaledValue>m_fScaledVisibleMax) + { + if( !approxEqual(fScaledValue,m_fScaledVisibleMax) ) + return false; + } + if(fScaledValue<m_fScaledVisibleMin) + { + if( !approxEqual(fScaledValue,m_fScaledVisibleMin) ) + return false; + } + return true; +} + +void TickmarkHelper::getAllTicks( ::std::vector< ::std::vector< TickInfo > >& 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 ); + + aAllTicks.realloc(nDepthCount); + aAllTicks[0].realloc(nMaxMajorTickCount); + + sal_Int32 nRealMajorTickCount = 0; + double* pValue = NULL; + for( sal_Int32 nMajorTick=0; nMajorTick<nMaxMajorTickCount; nMajorTick++ ) + { + pValue = this->getMajorTick( 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; nN<nDepth; nN++) + { + if( m_rIncrement.SubIncrements[nN].IntervalCount>1 ) + nCheckCount *= m_rIncrement.SubIncrements[nN].IntervalCount; + } + uno::Sequence< double >& rTicks = aAllTicks[nDepth]; + sal_Int32 nCount = rTicks.getLength(); + //check lower border + for( nTick=0; nTick<nCheckCount && nTick<nCount; nTick++) + { + if( !isVisible( rTicks[nTick] ) ) + nInvisibleAtLowerBorder++; + } + //check upper border + for( nTick=nCount-1; nTick>nCount-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<nNewCount; nTick++) + rTicks[nTick] = aOldTicks[nInvisibleAtLowerBorder+nTick]; + } + } + + //fill return value + rAllTickInfos.resize(aAllTicks.getLength()); + for( nDepth=0 ;nDepth<aAllTicks.getLength(); nDepth++ ) + { + sal_Int32 nCount = aAllTicks[nDepth].getLength(); + rAllTickInfos[nDepth].resize( nCount ); + for(sal_Int32 nN = 0; nN<nCount; nN++) + { + rAllTickInfos[nDepth][nN].fScaledTickValue = aAllTicks[nDepth][nN]; + } + } +} + +void TickmarkHelper::getAllTicksShifted( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const +{ + std::auto_ptr< TickmarkHelper > apShiftedTickmarkHelper( createShiftedTickmarkHelper() ); + apShiftedTickmarkHelper->getAllTicks( rAllTickInfos ); +} + +void TickmarkHelper::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; nPartTick<nIntervalCount; nPartTick++ ) + { + pValue = this->getMinorTick( nPartTick, nDepth + , fLastParentTick, *pfNextParentTick ); + if(!pValue) + continue; + + aSubTicks[nRealSubTickCount] = *pValue; + nRealSubTickCount++; + } + } + + aSubTicks.realloc(nRealSubTickCount); + rParentTicks[nDepth] = aSubTicks; + if(m_rIncrement.SubIncrements.getLength()>nDepth) + addSubTicks( nDepth+1, rParentTicks ); +} + +//----------------------------------------------------------------------------- +// ___TickmarkHelper_2D___ +//----------------------------------------------------------------------------- +TickmarkHelper_2D::TickmarkHelper_2D( + const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement + //, double fStrech_SceneToScreen, double fOffset_SceneToScreen ) + , const B2DVector& rStartScreenPos, const B2DVector& rEndScreenPos + , const B2DVector& rAxisLineToLabelLineShift ) + : TickmarkHelper( rScale, rIncrement ) + , m_aAxisStartScreenPosition2D(rStartScreenPos) + , m_aAxisEndScreenPosition2D(rEndScreenPos) + , m_aAxisLineToLabelLineShift(rAxisLineToLabelLineShift) + , m_fStrech_LogicToScreen(1.0) + , m_fOffset_LogicToScreen(0.0) +{ + double fWidthY = m_fScaledVisibleMax - m_fScaledVisibleMin; + if( AxisOrientation_MATHEMATICAL==m_rScale.Orientation ) + { + m_fStrech_LogicToScreen = 1.0/fWidthY; + m_fOffset_LogicToScreen = -m_fScaledVisibleMin; + } + else + { + B2DVector aSwap(m_aAxisStartScreenPosition2D); + m_aAxisStartScreenPosition2D = m_aAxisEndScreenPosition2D; + m_aAxisEndScreenPosition2D = aSwap; + + m_fStrech_LogicToScreen = -1.0/fWidthY; + m_fOffset_LogicToScreen = -m_fScaledVisibleMax; + } +} + +TickmarkHelper* TickmarkHelper_2D::createShiftedTickmarkHelper() const +{ + ExplicitIncrementData aShiftedIncrement( m_rIncrement ); + aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0; + + ::basegfx::B2DVector aStart( m_aAxisStartScreenPosition2D ); + ::basegfx::B2DVector aEnd( m_aAxisEndScreenPosition2D ); + if( AxisOrientation_MATHEMATICAL==m_rScale.Orientation ) + std::swap( aStart, aEnd ); + + return new TickmarkHelper_2D( m_rScale, aShiftedIncrement, aStart, aEnd, m_aAxisLineToLabelLineShift ); +} + +TickmarkHelper_2D::~TickmarkHelper_2D() +{ +} + +bool TickmarkHelper_2D::isHorizontalAxis() const +{ + return ( m_aAxisStartScreenPosition2D.getY() == m_aAxisEndScreenPosition2D.getY() ); +} +bool TickmarkHelper_2D::isVerticalAxis() const +{ + return ( m_aAxisStartScreenPosition2D.getX() == m_aAxisEndScreenPosition2D.getX() ); +} + +//static +sal_Int32 TickmarkHelper_2D::getTickScreenDistance( TickIter& rIter ) +{ + //return the positive distance between the two first tickmarks in screen values + //if there are less than two tickmarks -1 is returned + + const TickInfo* pFirstTickInfo = rIter.firstInfo(); + const TickInfo* pSecondTickInfo = rIter.nextInfo(); + if(!pSecondTickInfo || !pFirstTickInfo) + return -1; + + return pFirstTickInfo->getScreenDistanceBetweenTicks( *pSecondTickInfo ); +} + +B2DVector TickmarkHelper_2D::getTickScreenPosition2D( double fScaledLogicTickValue ) const +{ + B2DVector aRet(m_aAxisStartScreenPosition2D); + aRet += (m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D) + *((fScaledLogicTickValue+m_fOffset_LogicToScreen)*m_fStrech_LogicToScreen); + return aRet; +} + +void TickmarkHelper_2D::addPointSequenceForTickLine( drawing::PointSequenceSequence& rPoints + , sal_Int32 nSequenceIndex + , double fScaledLogicTickValue, double fInnerDirectionSign + , const TickmarkProperties& rTickmarkProperties + , bool bPlaceAtLabels ) const +{ + if( fInnerDirectionSign==0.0 ) + fInnerDirectionSign = 1.0; + + B2DVector aTickScreenPosition = this->getTickScreenPosition2D(fScaledLogicTickValue); + if( bPlaceAtLabels ) + aTickScreenPosition += m_aAxisLineToLabelLineShift; + + B2DVector aMainDirection = m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D; + aMainDirection.normalize(); + B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX()); + aOrthoDirection *= fInnerDirectionSign; + aOrthoDirection.normalize(); + + B2DVector aStart = aTickScreenPosition + aOrthoDirection*rTickmarkProperties.RelativePos; + B2DVector aEnd = aStart - aOrthoDirection*rTickmarkProperties.Length; + + rPoints[nSequenceIndex].realloc(2); + rPoints[nSequenceIndex][0].X = static_cast<sal_Int32>(aStart.getX()); + rPoints[nSequenceIndex][0].Y = static_cast<sal_Int32>(aStart.getY()); + rPoints[nSequenceIndex][1].X = static_cast<sal_Int32>(aEnd.getX()); + rPoints[nSequenceIndex][1].Y = static_cast<sal_Int32>(aEnd.getY()); +} + +B2DVector TickmarkHelper_2D::getDistanceAxisTickToText( const AxisProperties& rAxisProperties, bool bIncludeFarAwayDistanceIfSo, bool bIncludeSpaceBetweenTickAndText ) const +{ + bool bFarAwayLabels = false; + if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START == rAxisProperties.m_eLabelPos + || ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END == rAxisProperties.m_eLabelPos ) + bFarAwayLabels = true; + + double fInnerDirectionSign = rAxisProperties.m_fInnerDirectionSign; + if( fInnerDirectionSign==0.0 ) + fInnerDirectionSign = 1.0; + + B2DVector aMainDirection = m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D; + aMainDirection.normalize(); + B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX()); + aOrthoDirection *= fInnerDirectionSign; + aOrthoDirection.normalize(); + + B2DVector aStart(0,0), aEnd(0,0); + if( bFarAwayLabels ) + { + TickmarkProperties aProps( AxisProperties::getBiggestTickmarkProperties() ); + aStart = aOrthoDirection*aProps.RelativePos; + aEnd = aStart - aOrthoDirection*aProps.Length; + } + else + { + for( sal_Int32 nN=rAxisProperties.m_aTickmarkPropertiesList.size();nN--;) + { + const TickmarkProperties& rProps = rAxisProperties.m_aTickmarkPropertiesList[nN]; + B2DVector aNewStart = aOrthoDirection*rProps.RelativePos; + B2DVector aNewEnd = aNewStart - aOrthoDirection*rProps.Length; + if(aNewStart.getLength()>aStart.getLength()) + aStart=aNewStart; + if(aNewEnd.getLength()>aEnd.getLength()) + aEnd=aNewEnd; + } + } + + B2DVector aLabelDirection(aStart); + if( rAxisProperties.m_fInnerDirectionSign != rAxisProperties.m_fLabelDirectionSign ) + aLabelDirection = aEnd; + + B2DVector aOrthoLabelDirection(aOrthoDirection); + if( rAxisProperties.m_fInnerDirectionSign != rAxisProperties.m_fLabelDirectionSign ) + aOrthoLabelDirection*=-1.0; + aOrthoLabelDirection.normalize(); + if( bIncludeSpaceBetweenTickAndText ) + aLabelDirection += aOrthoLabelDirection*AXIS2D_TICKLABELSPACING; + if( bFarAwayLabels && bIncludeFarAwayDistanceIfSo ) + aLabelDirection += m_aAxisLineToLabelLineShift; + return aLabelDirection; +} + +void TickmarkHelper_2D::createPointSequenceForAxisMainLine( drawing::PointSequenceSequence& rPoints ) const +{ + rPoints[0].realloc(2); + rPoints[0][0].X = static_cast<sal_Int32>(m_aAxisStartScreenPosition2D.getX()); + rPoints[0][0].Y = static_cast<sal_Int32>(m_aAxisStartScreenPosition2D.getY()); + rPoints[0][1].X = static_cast<sal_Int32>(m_aAxisEndScreenPosition2D.getX()); + rPoints[0][1].Y = static_cast<sal_Int32>(m_aAxisEndScreenPosition2D.getY()); +} + +void TickmarkHelper_2D::updateScreenValues( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const +{ + //get the transformed screen values for all tickmarks in rAllTickInfos + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = rAllTickInfos.begin(); + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = rAllTickInfos.end(); + for( ; aDepthIter != aDepthEnd; aDepthIter++ ) + { + ::std::vector< TickInfo >::iterator aTickIter = (*aDepthIter).begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = (*aDepthIter).end(); + for( ; aTickIter != aTickEnd; aTickIter++ ) + { + TickInfo& rTickInfo = (*aTickIter); + rTickInfo.aTickScreenPosition = + this->getTickScreenPosition2D( rTickInfo.fScaledTickValue ); + } + } +} + +//----------------------------------------------------------------------------- +// ___TickmarkHelper_3D___ +//----------------------------------------------------------------------------- +TickmarkHelper_3D::TickmarkHelper_3D( + const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement ) + : TickmarkHelper( rScale, rIncrement ) +{ +} + +TickmarkHelper* TickmarkHelper_3D::createShiftedTickmarkHelper() const +{ + ExplicitIncrementData aShiftedIncrement( m_rIncrement ); + aShiftedIncrement.BaseValue = m_rIncrement.BaseValue-m_rIncrement.Distance/2.0; + return new TickmarkHelper_3D( m_rScale, aShiftedIncrement ); +} + +TickmarkHelper_3D::~TickmarkHelper_3D() +{ +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/TickmarkHelper.hxx b/chart2/source/view/axes/TickmarkHelper.hxx new file mode 100644 index 000000000000..78fc2fe1c502 --- /dev/null +++ b/chart2/source/view/axes/TickmarkHelper.hxx @@ -0,0 +1,276 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_TICKMARKHELPER_HXX +#define _CHART2_TICKMARKHELPER_HXX + +#include "TickmarkProperties.hxx" +#include "VAxisProperties.hxx" +#include <com/sun/star/chart2/ExplicitIncrementData.hpp> +#include <com/sun/star/chart2/ExplicitScaleData.hpp> +#include <basegfx/vector/b2dvector.hxx> +#include <com/sun/star/drawing/PointSequenceSequence.hpp> +#include <com/sun/star/drawing/XShape.hpp> +#include <com/sun/star/uno/Sequence.h> + +#include <vector> + +//............................................................................. +namespace chart +{ +//............................................................................. + +using ::basegfx::B2DVector; +//----------------------------------------------------------------------------- +/** +*/ + +struct TickInfo +{ + double fScaledTickValue; + double fUnscaledTickValue; + + ::basegfx::B2DVector aTickScreenPosition; + bool bPaintIt; + + ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape > xTextShape; + + rtl::OUString aText;//used only for complex categories so far + sal_Int32 nFactorForLimitedTextWidth;//categories in higher levels of complex categories can have more place than a single simple category + +//methods: + TickInfo(); + void updateUnscaledValue( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XScaling >& xInverseScaling ); + + sal_Int32 getScreenDistanceBetweenTicks( const TickInfo& rOherTickInfo ) const; +}; +class TickIter +{ +public: + virtual ~TickIter(){}; + virtual TickInfo* firstInfo()=0; + virtual TickInfo* nextInfo()=0; +}; + +class PureTickIter : public TickIter +{ +public: + PureTickIter( ::std::vector< TickInfo >& rTickInfoVector ); + virtual ~PureTickIter(); + virtual TickInfo* firstInfo(); + virtual TickInfo* nextInfo(); + +private: + ::std::vector< TickInfo >& m_rTickVector; + ::std::vector< TickInfo >::iterator m_aTickIter; +}; + +class EquidistantTickIter : public TickIter +{ +public: + EquidistantTickIter( const ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Sequence< double > >& rTicks + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , sal_Int32 nMinDepth=0, sal_Int32 nMaxDepth=-1 ); + EquidistantTickIter( ::std::vector< ::std::vector< TickInfo > >& rTickInfos + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , sal_Int32 nMinDepth=0, sal_Int32 nMaxDepth=-1 ); + virtual ~EquidistantTickIter(); + + virtual double* firstValue(); + virtual double* nextValue(); + + virtual TickInfo* firstInfo(); + virtual TickInfo* nextInfo(); + + sal_Int32 getCurrentDepth() const { return m_nCurrentDepth; } + +protected: + bool gotoIndex( sal_Int32 nTickIndex ); + sal_Int32 getCurrentIndex() const; + sal_Int32 getMaxIndex() const; + +private: //methods + sal_Int32 getIntervalCount( sal_Int32 nDepth ); + bool isAtLastPartTick(); + + void initIter( sal_Int32 nMinDepth, sal_Int32 nMaxDepth ); + sal_Int32 getStartDepth() const; + + bool gotoFirst(); + bool gotoNext(); + + + double getTickValue(sal_Int32 nDepth, sal_Int32 nIndex) const + { + if(m_pSimpleTicks) + return (*m_pSimpleTicks)[nDepth][nIndex]; + else + return (((*m_pInfoTicks)[nDepth])[nIndex]).fScaledTickValue; + } + sal_Int32 getTickCount( sal_Int32 nDepth ) const + { + if(m_pSimpleTicks) + return (*m_pSimpleTicks)[nDepth].getLength(); + else + return (*m_pInfoTicks)[nDepth].size(); + } + sal_Int32 getMaxDepth() const + { + if(m_pSimpleTicks) + return (*m_pSimpleTicks).getLength()-1; + else + return (*m_pInfoTicks).size()-1; + } + +private: //member + const ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Sequence< double > >* m_pSimpleTicks; + ::std::vector< ::std::vector< TickInfo > >* m_pInfoTicks; + const ::com::sun::star::chart2::ExplicitIncrementData& m_rIncrement; + //iteration from m_nMinDepth to m_nMaxDepth + sal_Int32 m_nMinDepth; + sal_Int32 m_nMaxDepth; + sal_Int32 m_nTickCount; + sal_Int32* m_pnPositions; //current positions in the different sequences + sal_Int32* m_pnPreParentCount; //the tickmarks do not start with a major tick always, + //the PreParentCount states for each depth how many subtickmarks are available in front of the first parent tickmark + bool* m_pbIntervalFinished; + sal_Int32 m_nCurrentDepth; + sal_Int32 m_nCurrentPos; + double m_fCurrentValue; +}; + +class TickmarkHelper +{ +public: + TickmarkHelper( + const ::com::sun::star::chart2::ExplicitScaleData& rScale + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement ); + virtual ~TickmarkHelper(); + + virtual TickmarkHelper* createShiftedTickmarkHelper() const; + + void getAllTicks( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const; + void getAllTicksShifted( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const; + + // + static double getMinimumAtIncrement( double fMin, const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement ); + static double getMaximumAtIncrement( double fMax, const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement ); + +protected: //methods + void addSubTicks( sal_Int32 nDepth, + ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Sequence< double > >& rParentTicks ) const; + double* getMajorTick( sal_Int32 nTick ) const; + double* getMinorTick( sal_Int32 nTick, sal_Int32 nDepth + , double fStartParentTick, double fNextParentTick ) const; + sal_Int32 getMaxTickCount( sal_Int32 nDepth = 0 ) const; + sal_Int32 getTickDepth() const; + bool isVisible( double fValue ) const; + bool isWithinOuterBorder( double fScaledValue ) const; //all within the outer major tick marks + + virtual void updateScreenValues( ::std::vector< ::std::vector< TickInfo > >& /*rAllTickInfos*/ ) const {} + +protected: //member + ::com::sun::star::chart2::ExplicitScaleData m_rScale; + ::com::sun::star::chart2::ExplicitIncrementData m_rIncrement; + + ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XScaling > + m_xInverseScaling; + double* m_pfCurrentValues; + //major-tick positions that may lay outside the visible range but complete partly visible intervals at the borders + double m_fOuterMajorTickBorderMin; + double m_fOuterMajorTickBorderMax; + double m_fOuterMajorTickBorderMin_Scaled; + double m_fOuterMajorTickBorderMax_Scaled; + + //minimum and maximum of the visible range after scaling + double m_fScaledVisibleMin; + double m_fScaledVisibleMax; +}; + +class TickmarkHelper_2D : public TickmarkHelper +{ +public: + TickmarkHelper_2D( + const ::com::sun::star::chart2::ExplicitScaleData& rScale + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , const ::basegfx::B2DVector& rStartScreenPos, const ::basegfx::B2DVector& rEndScreenPos + , const ::basegfx::B2DVector& rAxisLineToLabelLineShift ); + //, double fStrech_SceneToScreen, double fOffset_SceneToScreen ); + virtual ~TickmarkHelper_2D(); + + virtual TickmarkHelper* createShiftedTickmarkHelper() const; + + static sal_Int32 getTickScreenDistance( TickIter& rIter ); + + void createPointSequenceForAxisMainLine( ::com::sun::star::drawing::PointSequenceSequence& rPoints ) const; + void addPointSequenceForTickLine( ::com::sun::star::drawing::PointSequenceSequence& rPoints + , sal_Int32 nSequenceIndex + , double fScaledLogicTickValue, double fInnerDirectionSign + , const TickmarkProperties& rTickmarkProperties, bool bPlaceAtLabels ) const; + ::basegfx::B2DVector getDistanceAxisTickToText( const AxisProperties& rAxisProperties + , bool bIncludeFarAwayDistanceIfSo = false + , bool bIncludeSpaceBetweenTickAndText = true ) const; + + virtual void updateScreenValues( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const; + + bool isHorizontalAxis() const; + bool isVerticalAxis() const; + +protected: //methods + ::basegfx::B2DVector getTickScreenPosition2D( double fScaledLogicTickValue ) const; + +private: //member + ::basegfx::B2DVector m_aAxisStartScreenPosition2D; + ::basegfx::B2DVector m_aAxisEndScreenPosition2D; + + //labels might be posioned high or low on the border of the diagram far away from the axis + //add this vector to go from the axis line to the label line (border of the diagram) + ::basegfx::B2DVector m_aAxisLineToLabelLineShift; + + double m_fStrech_LogicToScreen; + double m_fOffset_LogicToScreen; +}; + +class TickmarkHelper_3D : public TickmarkHelper +{ +public: + TickmarkHelper_3D( + const ::com::sun::star::chart2::ExplicitScaleData& rScale + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement ); + virtual ~TickmarkHelper_3D(); + + virtual TickmarkHelper* createShiftedTickmarkHelper() const; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/TickmarkProperties.hxx b/chart2/source/view/axes/TickmarkProperties.hxx new file mode 100644 index 000000000000..09c0179d3698 --- /dev/null +++ b/chart2/source/view/axes/TickmarkProperties.hxx @@ -0,0 +1,53 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#ifndef _CHART2_VTICKMARKPROPERTIES_HXX +#define _CHART2_VTICKMARKPROPERTIES_HXX + +#include "VLineProperties.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +struct TickmarkProperties +{ + sal_Int32 RelativePos;//Position in screen values relative to the axis where the tickmark line starts + sal_Int32 Length;//Length of the tickmark line in screen values + + VLineProperties aLineProperties; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VAxisBase.cxx b/chart2/source/view/axes/VAxisBase.cxx new file mode 100644 index 000000000000..0159fb20cad6 --- /dev/null +++ b/chart2/source/view/axes/VAxisBase.cxx @@ -0,0 +1,253 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "VAxisBase.hxx" +#include "ShapeFactory.hxx" +#include "CommonConverters.hxx" +#include "TickmarkHelper.hxx" +#include "macros.hxx" + +// header for define DBG_ASSERT +#include <tools/debug.hxx> + +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; + +VAxisBase::VAxisBase( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , const AxisProperties& rAxisProperties + , const uno::Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier ) + : VAxisOrGridBase( nDimensionIndex, nDimensionCount ) + , m_xNumberFormatsSupplier( xNumberFormatsSupplier ) + , m_aAxisProperties( rAxisProperties ) + , m_bUseTextLabels( false ) + , m_bReCreateAllTickInfos( true ) + , m_bRecordMaximumTextSize(false) + , m_nMaximumTextWidthSoFar(0) + , m_nMaximumTextHeightSoFar(0) +{ +} + +VAxisBase::~VAxisBase() +{ +} + +sal_Int32 VAxisBase::getDimensionCount() +{ + return m_nDimension; +} + +void SAL_CALL VAxisBase::initAxisLabelProperties( const ::com::sun::star::awt::Size& rFontReferenceSize + , const ::com::sun::star::awt::Rectangle& rMaximumSpaceForLabels ) +{ + m_aAxisLabelProperties.m_aFontReferenceSize = rFontReferenceSize; + m_aAxisLabelProperties.m_aMaximumSpaceForLabels = rMaximumSpaceForLabels; + + if( !m_aAxisProperties.m_bDisplayLabels ) + return; + + if( AxisType::SERIES==m_aAxisProperties.m_nAxisType ) + { + if( m_aAxisProperties.m_xAxisTextProvider.is() ) + m_aTextLabels = m_aAxisProperties.m_xAxisTextProvider->getTextualData(); + + m_bUseTextLabels = true; + if( m_aTextLabels.getLength() == 1 ) + { + //don't show a single series name + m_aAxisProperties.m_bDisplayLabels = false; + return; + } + } + else if( AxisType::CATEGORY==m_aAxisProperties.m_nAxisType ) + { + if( m_aAxisProperties.m_pExplicitCategoriesProvider ) + m_aTextLabels = m_aAxisProperties.m_pExplicitCategoriesProvider->getSimpleCategories(); + + m_bUseTextLabels = true; + } + + m_aAxisLabelProperties.nNumberFormatKey = m_aAxisProperties.m_nNumberFormatKey; + m_aAxisLabelProperties.init(m_aAxisProperties.m_xAxisModel); + if( m_aAxisProperties.m_bComplexCategories && AxisType::CATEGORY == m_aAxisProperties.m_nAxisType ) + m_aAxisLabelProperties.eStaggering = SIDE_BY_SIDE; +} + +void VAxisBase::recordMaximumTextSize( const Reference< drawing::XShape >& xShape, double fRotationAngleDegree ) +{ + if( m_bRecordMaximumTextSize && xShape.is() ) + { + awt::Size aSize( ShapeFactory::getSizeAfterRotation( + xShape, fRotationAngleDegree ) ); + + m_nMaximumTextWidthSoFar = std::max( m_nMaximumTextWidthSoFar, aSize.Width ); + m_nMaximumTextHeightSoFar = std::max( m_nMaximumTextHeightSoFar, aSize.Height ); + } +} + +sal_Int32 VAxisBase::estimateMaximumAutoMainIncrementCount() +{ + return 10; +} + +void VAxisBase::setExrtaLinePositionAtOtherAxis( const double& fCrossingAt ) +{ + if( m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis ) + delete m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis; + m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis = new double(fCrossingAt); +} + +sal_Bool SAL_CALL VAxisBase::isAnythingToDraw() +{ + if( !m_aAxisProperties.m_xAxisModel.is() ) + return false; + + DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"Axis is not proper initialized"); + if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is())) + return false; + + uno::Reference< beans::XPropertySet > xProps( m_aAxisProperties.m_xAxisModel, uno::UNO_QUERY ); + if( xProps.is() ) + { + sal_Bool bShow = sal_False; + xProps->getPropertyValue( C2U( "Show" ) ) >>= bShow; + if( !bShow ) + return false; + } + return true; +} + +void SAL_CALL VAxisBase::setExplicitScaleAndIncrement( + const ExplicitScaleData& rScale + , const ExplicitIncrementData& rIncrement ) + throw (uno::RuntimeException) +{ + m_bReCreateAllTickInfos = true; + m_aScale = rScale; + m_aIncrement = rIncrement; +} + +void VAxisBase::createAllTickInfos( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) +{ + std::auto_ptr< TickmarkHelper > apTickmarkHelper( this->createTickmarkHelper() ); + apTickmarkHelper->getAllTicks( rAllTickInfos ); +} + +bool VAxisBase::prepareShapeCreation() +{ + //returns true if all is ready for further shape creation and any shapes need to be created + if( !isAnythingToDraw() ) + return false; + + if( m_bReCreateAllTickInfos ) + { + //----------------------------------------- + //create all scaled tickmark values + removeTextShapesFromTicks(); + + createAllTickInfos(m_aAllTickInfos); + m_bReCreateAllTickInfos = false; + } + + if( m_xGroupShape_Shapes.is() ) + return true; + + //----------------------------------------- + //create named group shape + m_xGroupShape_Shapes = this->createGroupShape( m_xLogicTarget, m_nDimension==2 ? m_aCID : C2U("")); + + if( m_aAxisProperties.m_bDisplayLabels ) + m_xTextTarget = m_pShapeFactory->createGroup2D( m_xFinalTarget, m_aCID ); + + return true; +} + +sal_Int32 VAxisBase::getIndexOfLongestLabel( const uno::Sequence< rtl::OUString >& rLabels ) +{ + sal_Int32 nRet = 0; + sal_Int32 nLength = 0; + sal_Int32 nN = 0; + for( nN=0; nN<rLabels.getLength(); nN++ ) + { + //todo: get real text width (without creating shape) instead of character count + if( rLabels[nN].getLength() > nLength ) + { + nLength = rLabels[nN].getLength(); + nRet = nN; + } + } + return nRet; +} + +void VAxisBase::removeTextShapesFromTicks() +{ + if( m_xTextTarget.is() ) + { + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = m_aAllTickInfos.begin(); + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = m_aAllTickInfos.end(); + for( ; aDepthIter != aDepthEnd; aDepthIter++ ) + { + ::std::vector< TickInfo >::iterator aTickIter = (*aDepthIter).begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = (*aDepthIter).end(); + for( ; aTickIter != aTickEnd; aTickIter++ ) + { + TickInfo& rTickInfo = (*aTickIter); + if(rTickInfo.xTextShape.is()) + { + m_xTextTarget->remove(rTickInfo.xTextShape); + rTickInfo.xTextShape = NULL; + } + } + } + } +} + +void VAxisBase::updateUnscaledValuesAtTicks( TickIter& rIter ) +{ + Reference< XScaling > xInverseScaling( NULL ); + if( m_aScale.Scaling.is() ) + xInverseScaling = m_aScale.Scaling->getInverseScaling(); + + for( TickInfo* pTickInfo = rIter.firstInfo() + ; pTickInfo; pTickInfo = rIter.nextInfo() ) + { + pTickInfo->updateUnscaledValue( xInverseScaling ); + } +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VAxisBase.hxx b/chart2/source/view/axes/VAxisBase.hxx new file mode 100644 index 000000000000..ba7e94b2070c --- /dev/null +++ b/chart2/source/view/axes/VAxisBase.hxx @@ -0,0 +1,107 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VAXISBASE_HXX +#define _CHART2_VAXISBASE_HXX + +#include "VAxisOrGridBase.hxx" +#include "VAxisProperties.hxx" +#include "TickmarkHelper.hxx" +#include <com/sun/star/util/XNumberFormatsSupplier.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class VAxisBase : public VAxisOrGridBase +{ +public: + VAxisBase( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , const AxisProperties& rAxisProperties + , const ::com::sun::star::uno::Reference< + ::com::sun::star::util::XNumberFormatsSupplier >& xNumberFormatsSupplier ); + virtual ~VAxisBase(); + + sal_Int32 getDimensionCount(); + + virtual void SAL_CALL createMaximumLabels()=0; + virtual void SAL_CALL createLabels()=0; + virtual void SAL_CALL updatePositions()=0; + + virtual sal_Bool SAL_CALL isAnythingToDraw(); + virtual void SAL_CALL initAxisLabelProperties( + const ::com::sun::star::awt::Size& rFontReferenceSize + , const ::com::sun::star::awt::Rectangle& rMaximumSpaceForLabels ); + + virtual void SAL_CALL setExplicitScaleAndIncrement( + const ::com::sun::star::chart2::ExplicitScaleData& rScale + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement ) + throw (::com::sun::star::uno::RuntimeException); + + virtual sal_Int32 estimateMaximumAutoMainIncrementCount(); + virtual void createAllTickInfos( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ); + + void setExrtaLinePositionAtOtherAxis( const double& fCrossingAt ); + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- +protected: //methods + sal_Int32 getIndexOfLongestLabel( const ::com::sun::star::uno::Sequence< rtl::OUString >& rLabels ); + void removeTextShapesFromTicks(); + void updateUnscaledValuesAtTicks( TickIter& rIter ); + + virtual bool prepareShapeCreation(); + void recordMaximumTextSize( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShape >& xShape + , double fRotationAngleDegree ); + +protected: //member + ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier > m_xNumberFormatsSupplier; + AxisProperties m_aAxisProperties; + AxisLabelProperties m_aAxisLabelProperties; + ::com::sun::star::uno::Sequence< rtl::OUString > m_aTextLabels; + bool m_bUseTextLabels; + + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes > m_xGroupShape_Shapes; + ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes > m_xTextTarget; + + ::std::vector< ::std::vector< TickInfo > > m_aAllTickInfos; + bool m_bReCreateAllTickInfos; + + bool m_bRecordMaximumTextSize; + sal_Int32 m_nMaximumTextWidthSoFar; + sal_Int32 m_nMaximumTextHeightSoFar; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VAxisOrGridBase.cxx b/chart2/source/view/axes/VAxisOrGridBase.cxx new file mode 100644 index 000000000000..f2e2e90c1045 --- /dev/null +++ b/chart2/source/view/axes/VAxisOrGridBase.cxx @@ -0,0 +1,98 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "VAxisOrGridBase.hxx" +#include "ShapeFactory.hxx" +#include "CommonConverters.hxx" +#include "TickmarkHelper.hxx" + +// header for define DBG_ASSERT +#include <tools/debug.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; + +VAxisOrGridBase::VAxisOrGridBase( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount ) + : PlotterBase( nDimensionCount ) + , m_nDimensionIndex( nDimensionIndex ) + , m_eLeftWallPos(CuboidPlanePosition_Left) + , m_eBackWallPos(CuboidPlanePosition_Back) + , m_eBottomPos(CuboidPlanePosition_Bottom) +{ +} + +VAxisOrGridBase::~VAxisOrGridBase() +{ +} + +void SAL_CALL VAxisOrGridBase::setExplicitScaleAndIncrement( + const ExplicitScaleData& rScale + , const ExplicitIncrementData& rIncrement ) + throw (uno::RuntimeException) +{ + m_aScale = rScale; + m_aIncrement = rIncrement; +} + +void VAxisOrGridBase::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix ) +{ + m_aMatrixScreenToScene = HomogenMatrixToB3DHomMatrix(rMatrix); + PlotterBase::setTransformationSceneToScreen( rMatrix); +} + +void VAxisOrGridBase::set3DWallPositions( CuboidPlanePosition eLeftWallPos, CuboidPlanePosition eBackWallPos, CuboidPlanePosition eBottomPos ) +{ + m_eLeftWallPos = eLeftWallPos; + m_eBackWallPos = eBackWallPos; + m_eBottomPos = eBottomPos; +} + +TickmarkHelper* VAxisOrGridBase::createTickmarkHelper() +{ + TickmarkHelper* pRet=NULL; + if( 2==m_nDimension ) + { + pRet = new TickmarkHelper( m_aScale, m_aIncrement ); + } + else + { + pRet = new TickmarkHelper_3D( m_aScale, m_aIncrement ); + } + return pRet; +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VAxisOrGridBase.hxx b/chart2/source/view/axes/VAxisOrGridBase.hxx new file mode 100644 index 000000000000..35b32b2b1c03 --- /dev/null +++ b/chart2/source/view/axes/VAxisOrGridBase.hxx @@ -0,0 +1,83 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VAXISORGRIDBASE_HXX +#define _CHART2_VAXISORGRIDBASE_HXX + +#include "PlotterBase.hxx" +#include "ThreeDHelper.hxx" + +#include <com/sun/star/chart2/ExplicitIncrementData.hpp> +#include <com/sun/star/chart2/ExplicitScaleData.hpp> +#include <com/sun/star/drawing/HomogenMatrix.hpp> +#include <com/sun/star/drawing/XShapes.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <basegfx/matrix/b3dhommatrix.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ +class ShapeFactory; +class TickmarkHelper; + +class VAxisOrGridBase : public PlotterBase +{ +public: + VAxisOrGridBase( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount ); + virtual ~VAxisOrGridBase(); + + virtual void setTransformationSceneToScreen( const ::com::sun::star::drawing::HomogenMatrix& rMatrix ); + virtual void SAL_CALL setExplicitScaleAndIncrement( + const ::com::sun::star::chart2::ExplicitScaleData& rScale + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement ) + throw (::com::sun::star::uno::RuntimeException); + void set3DWallPositions( CuboidPlanePosition eLeftWallPos, CuboidPlanePosition eBackWallPos, CuboidPlanePosition eBottomPos ); + + virtual TickmarkHelper* createTickmarkHelper(); + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- +protected: //member + ::com::sun::star::chart2::ExplicitScaleData m_aScale; + ::com::sun::star::chart2::ExplicitIncrementData m_aIncrement; + sal_Int32 m_nDimensionIndex; + + ::basegfx::B3DHomMatrix m_aMatrixScreenToScene; + + CuboidPlanePosition m_eLeftWallPos; + CuboidPlanePosition m_eBackWallPos; + CuboidPlanePosition m_eBottomPos; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VAxisProperties.cxx b/chart2/source/view/axes/VAxisProperties.cxx new file mode 100644 index 000000000000..1e3e62894741 --- /dev/null +++ b/chart2/source/view/axes/VAxisProperties.cxx @@ -0,0 +1,486 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "VAxisProperties.hxx" +#include "macros.hxx" +#include "ViewDefines.hxx" +#include "CommonConverters.hxx" +#include "AxisHelper.hxx" +#include "DiagramHelper.hxx" +#include "ChartModelHelper.hxx" + +#include <tools/color.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/chart/ChartAxisArrangeOrderType.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/text/WritingMode2.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +sal_Int32 lcl_calcTickLengthForDepth(sal_Int32 nDepth,sal_Int32 nTickmarkStyle) +{ + sal_Int32 nWidth = AXIS2D_TICKLENGTH; //@maybefuturetodo this length could be offered by the model + double fPercent = 1.0; + switch(nDepth) + { + case 0: + fPercent = 1.0; + break; + case 1: + fPercent = 0.75;//percentage like in the old chart + break; + case 2: + fPercent = 0.5; + break; + default: + fPercent = 0.3; + break; + } + if(nTickmarkStyle==3)//inner and outer tickmarks + fPercent*=2.0; + return static_cast<sal_Int32>(nWidth*fPercent); +} + +double lcl_getTickOffset(sal_Int32 nLength,sal_Int32 nTickmarkStyle) +{ + double fPercent = 0.0; //0<=fPercent<=1 + //0.0: completly inner + //1.0: completly outer + //0.5: half and half + + /* + nTickmarkStyle: + 1: inner tickmarks + 2: outer tickmarks + 3: inner and outer tickmarks + */ + switch(nTickmarkStyle) + { + case 1: + fPercent = 0.0; + break; + case 2: + fPercent = 1.0; + break; + default: + fPercent = 0.5; + break; + } + return fPercent*nLength; +} + +VLineProperties AxisProperties::makeLinePropertiesForDepth( sal_Int32 /* nDepth */ ) const +{ + //@todo get this from somewhere; maybe for each subincrement + //so far the model does not offer different settings for each tick depth + return m_aLineProperties; +} + +TickmarkProperties AxisProperties::makeTickmarkProperties( + sal_Int32 nDepth ) const +{ + /* + nTickmarkStyle: + 1: inner tickmarks + 2: outer tickmarks + 3: inner and outer tickmarks + */ + sal_Int32 nTickmarkStyle = 1; + if(nDepth==0) + { + nTickmarkStyle = m_nMajorTickmarks; + if(!nTickmarkStyle) + { + //create major tickmarks as if they were minor tickmarks + nDepth = 1; + nTickmarkStyle = m_nMinorTickmarks; + } + } + else if( nDepth==1) + { + nTickmarkStyle = m_nMinorTickmarks; + } + + if( m_fInnerDirectionSign == 0.0 ) + { + if( nTickmarkStyle != 0 ) + nTickmarkStyle = 3; //inner and outer tickmarks + } + + TickmarkProperties aTickmarkProperties; + aTickmarkProperties.Length = lcl_calcTickLengthForDepth(nDepth,nTickmarkStyle); + aTickmarkProperties.RelativePos = static_cast<sal_Int32>(lcl_getTickOffset(aTickmarkProperties.Length,nTickmarkStyle)); + aTickmarkProperties.aLineProperties = this->makeLinePropertiesForDepth( nDepth ); + return aTickmarkProperties; +} + +TickmarkProperties AxisProperties::makeTickmarkPropertiesForComplexCategories( + sal_Int32 nTickLength, sal_Int32 nTickStartDistanceToAxis, sal_Int32 /*nTextLevel*/ ) const +{ + sal_Int32 nTickmarkStyle = (m_fLabelDirectionSign==m_fInnerDirectionSign) ? 2/*outside*/ : 1/*inside*/; + + TickmarkProperties aTickmarkProperties; + aTickmarkProperties.Length = nTickLength;// + nTextLevel*( lcl_calcTickLengthForDepth(0,nTickmarkStyle) ); + aTickmarkProperties.RelativePos = static_cast<sal_Int32>(lcl_getTickOffset(aTickmarkProperties.Length+nTickStartDistanceToAxis,nTickmarkStyle)); + aTickmarkProperties.aLineProperties = this->makeLinePropertiesForDepth( 0 ); + return aTickmarkProperties; +} + +//static +TickmarkProperties AxisProperties::getBiggestTickmarkProperties() +{ + TickmarkProperties aTickmarkProperties; + sal_Int32 nDepth = 0; + sal_Int32 nTickmarkStyle = 3;//inner and outer tickmarks + aTickmarkProperties.Length = lcl_calcTickLengthForDepth( nDepth,nTickmarkStyle ); + aTickmarkProperties.RelativePos = static_cast<sal_Int32>( lcl_getTickOffset( aTickmarkProperties.Length, nTickmarkStyle ) ); + return aTickmarkProperties; +} + +//-------------------------------------------------------------------------- + +AxisProperties::AxisProperties( const uno::Reference< XAxis >& xAxisModel + , ExplicitCategoriesProvider* pExplicitCategoriesProvider ) + : m_xAxisModel(xAxisModel) + , m_nDimensionIndex(0) + , m_bIsMainAxis(true) + , m_bSwapXAndY(false) + , m_eCrossoverType( ::com::sun::star::chart::ChartAxisPosition_ZERO ) + , m_eLabelPos( ::com::sun::star::chart::ChartAxisLabelPosition_NEAR_AXIS ) + , m_eTickmarkPos( ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS ) + , m_pfMainLinePositionAtOtherAxis(NULL) + , m_pfExrtaLinePositionAtOtherAxis(NULL) + , m_bCrossingAxisHasReverseDirection(false) + , m_bCrossingAxisIsCategoryAxes(false) + , m_bAxisBetweenCategories(false) + , m_fLabelDirectionSign(1.0) + , m_fInnerDirectionSign(1.0) + , m_aLabelAlignment(LABEL_ALIGN_RIGHT_TOP) + , m_bDisplayLabels( true ) + , m_nNumberFormatKey(0) + , m_nMajorTickmarks(1) + , m_nMinorTickmarks(1) + , m_aTickmarkPropertiesList() + , m_aLineProperties() + //for category axes + , m_nAxisType(AxisType::REALNUMBER) + , m_bComplexCategories(false) + , m_pExplicitCategoriesProvider(pExplicitCategoriesProvider) + , m_xAxisTextProvider(0) +{ +} + +AxisProperties::AxisProperties( const AxisProperties& rAxisProperties ) + : m_xAxisModel( rAxisProperties.m_xAxisModel ) + , m_nDimensionIndex( m_nDimensionIndex ) + , m_bIsMainAxis( rAxisProperties.m_bIsMainAxis ) + , m_bSwapXAndY( rAxisProperties.m_bSwapXAndY ) + , m_eCrossoverType( rAxisProperties.m_eCrossoverType ) + , m_eLabelPos( rAxisProperties.m_eLabelPos ) + , m_eTickmarkPos( rAxisProperties.m_eTickmarkPos ) + , m_pfMainLinePositionAtOtherAxis( NULL ) + , m_pfExrtaLinePositionAtOtherAxis( NULL ) + , m_bCrossingAxisHasReverseDirection( rAxisProperties.m_bCrossingAxisHasReverseDirection ) + , m_bCrossingAxisIsCategoryAxes( rAxisProperties.m_bCrossingAxisIsCategoryAxes ) + , m_bAxisBetweenCategories( rAxisProperties.m_bAxisBetweenCategories ) + , m_fLabelDirectionSign( rAxisProperties.m_fLabelDirectionSign ) + , m_fInnerDirectionSign( rAxisProperties.m_fInnerDirectionSign ) + , m_aLabelAlignment( rAxisProperties.m_aLabelAlignment ) + , m_bDisplayLabels( rAxisProperties.m_bDisplayLabels ) + , m_nNumberFormatKey( rAxisProperties.m_nNumberFormatKey ) + , m_nMajorTickmarks( rAxisProperties.m_nMajorTickmarks ) + , m_nMinorTickmarks( rAxisProperties.m_nMinorTickmarks ) + , m_aTickmarkPropertiesList( rAxisProperties.m_aTickmarkPropertiesList ) + , m_aLineProperties( rAxisProperties.m_aLineProperties ) + //for category axes + , m_nAxisType( rAxisProperties.m_nAxisType ) + , m_bComplexCategories( rAxisProperties.m_bComplexCategories ) + , m_pExplicitCategoriesProvider( rAxisProperties.m_pExplicitCategoriesProvider ) + , m_xAxisTextProvider( rAxisProperties.m_xAxisTextProvider ) +{ + if( rAxisProperties.m_pfMainLinePositionAtOtherAxis ) + m_pfMainLinePositionAtOtherAxis = new double(*rAxisProperties.m_pfMainLinePositionAtOtherAxis); + if( rAxisProperties.m_pfExrtaLinePositionAtOtherAxis ) + m_pfExrtaLinePositionAtOtherAxis = new double (*rAxisProperties.m_pfExrtaLinePositionAtOtherAxis); +} + +AxisProperties::~AxisProperties() +{ + delete m_pfMainLinePositionAtOtherAxis; + delete m_pfExrtaLinePositionAtOtherAxis; +} + +LabelAlignment lcl_getLabelAlignmentForZAxis( const AxisProperties& rAxisProperties ) +{ + LabelAlignment aRet( LABEL_ALIGN_RIGHT ); + if( rAxisProperties.m_fLabelDirectionSign<0 ) + aRet = LABEL_ALIGN_LEFT; + return aRet; +} + +LabelAlignment lcl_getLabelAlignmentForYAxis( const AxisProperties& rAxisProperties ) +{ + LabelAlignment aRet( LABEL_ALIGN_RIGHT ); + if( rAxisProperties.m_fLabelDirectionSign<0 ) + aRet = LABEL_ALIGN_LEFT; + return aRet; +} + +LabelAlignment lcl_getLabelAlignmentForXAxis( const AxisProperties& rAxisProperties ) +{ + LabelAlignment aRet( LABEL_ALIGN_BOTTOM ); + if( rAxisProperties.m_fLabelDirectionSign<0 ) + aRet = LABEL_ALIGN_TOP; + return aRet; +} + +void AxisProperties::initAxisPositioning( const uno::Reference< beans::XPropertySet >& xAxisProp ) +{ + if( !xAxisProp.is() ) + return; + try + { + if( AxisHelper::isAxisPositioningEnabled() ) + { + xAxisProp->getPropertyValue(C2U( "CrossoverPosition" )) >>= m_eCrossoverType; + if( ::com::sun::star::chart::ChartAxisPosition_VALUE == m_eCrossoverType ) + { + double fValue = 0.0; + xAxisProp->getPropertyValue(C2U( "CrossoverValue" )) >>= fValue; + + if( m_bCrossingAxisIsCategoryAxes ) + { + fValue = ::rtl::math::round(fValue); + if( m_bAxisBetweenCategories ) + fValue-=0.5; + } + m_pfMainLinePositionAtOtherAxis = new double(fValue); + } + else if( ::com::sun::star::chart::ChartAxisPosition_ZERO == m_eCrossoverType ) + m_pfMainLinePositionAtOtherAxis = new double(0.0); + + xAxisProp->getPropertyValue(C2U( "LabelPosition" )) >>= m_eLabelPos; + xAxisProp->getPropertyValue(C2U( "MarkPosition" )) >>= m_eTickmarkPos; + } + else + { + m_eCrossoverType = ::com::sun::star::chart::ChartAxisPosition_START; + if( m_bIsMainAxis == m_bCrossingAxisHasReverseDirection ) + m_eCrossoverType = ::com::sun::star::chart::ChartAxisPosition_END; + m_eLabelPos = ::com::sun::star::chart::ChartAxisLabelPosition_NEAR_AXIS; + m_eTickmarkPos = ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS; + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } +} + +void AxisProperties::init( bool bCartesian ) +{ + uno::Reference< beans::XPropertySet > xProp = + uno::Reference<beans::XPropertySet>::query( this->m_xAxisModel ); + if( !xProp.is() ) + return; + + if( m_nDimensionIndex<2 ) + initAxisPositioning( xProp ); + + if( bCartesian ) + { + if( ::com::sun::star::chart::ChartAxisPosition_END == m_eCrossoverType ) + m_fInnerDirectionSign = m_bCrossingAxisHasReverseDirection ? 1 : -1; + else + m_fInnerDirectionSign = m_bCrossingAxisHasReverseDirection ? -1 : 1; + + if( ::com::sun::star::chart::ChartAxisLabelPosition_NEAR_AXIS == m_eLabelPos ) + m_fLabelDirectionSign = m_fInnerDirectionSign; + else if( ::com::sun::star::chart::ChartAxisLabelPosition_NEAR_AXIS_OTHER_SIDE == m_eLabelPos ) + m_fLabelDirectionSign = -m_fInnerDirectionSign; + else if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START == m_eLabelPos ) + m_fLabelDirectionSign = m_bCrossingAxisHasReverseDirection ? -1 : 1; + else if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END == m_eLabelPos ) + m_fLabelDirectionSign = m_bCrossingAxisHasReverseDirection ? 1 : -1; + + if( m_nDimensionIndex==2 ) + m_aLabelAlignment = lcl_getLabelAlignmentForZAxis(*this); + else + { + bool bIsYAxisPosition = (m_nDimensionIndex==1 && !m_bSwapXAndY) + || (m_nDimensionIndex==0 && m_bSwapXAndY); + if( bIsYAxisPosition ) + { + m_fLabelDirectionSign*=-1; + m_fInnerDirectionSign*=-1; + } + + if( bIsYAxisPosition ) + m_aLabelAlignment = lcl_getLabelAlignmentForYAxis(*this); + else + m_aLabelAlignment = lcl_getLabelAlignmentForXAxis(*this); + } + } + + try + { + //init LineProperties + m_aLineProperties.initFromPropertySet( xProp ); + + //init display labels + xProp->getPropertyValue( C2U( "DisplayLabels" ) ) >>= m_bDisplayLabels; + + //init categories + ScaleData aScaleData = m_xAxisModel->getScaleData(); + m_nAxisType = aScaleData.AxisType; + + //init TickmarkProperties + xProp->getPropertyValue( C2U( "MajorTickmarks" ) ) >>= m_nMajorTickmarks; + xProp->getPropertyValue( C2U( "MinorTickmarks" ) ) >>= m_nMinorTickmarks; + + sal_Int32 nMaxDepth = 0; + if(m_nMinorTickmarks!=0) + nMaxDepth=2; + else if(m_nMajorTickmarks!=0) + nMaxDepth=1; + + this->m_aTickmarkPropertiesList.clear(); + for( sal_Int32 nDepth=0; nDepth<nMaxDepth; nDepth++ ) + { + TickmarkProperties aTickmarkProperties = this->makeTickmarkProperties( nDepth ); + this->m_aTickmarkPropertiesList.push_back( aTickmarkProperties ); + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } +} + +//----------------------------------------------------------------------------- + +AxisLabelProperties::AxisLabelProperties() + : m_aFontReferenceSize( ChartModelHelper::getDefaultPageSize() ) + , m_aMaximumSpaceForLabels( 0 , 0, m_aFontReferenceSize.Width, m_aFontReferenceSize.Height ) + , nNumberFormatKey(0) + , eStaggering( SIDE_BY_SIDE ) + , bLineBreakAllowed( false ) + , bOverlapAllowed( false ) + , bStackCharacters( false ) + , fRotationAngleDegree( 0.0 ) + , nRhythm( 1 ) + , bRhythmIsFix(false) +{ + /* + aLocale.Language = C2U( "en" ); + aLocale.Country = C2U( "US" ); + + //aLocale.Language = C2U( "ar" ); + //aLocale.Country = C2U( "IR" ); + + //aLocale.Language = C2U( "ja" ); + //aLocale.Country = C2U( "JP" ); + */ +} + +void AxisLabelProperties::init( const uno::Reference< XAxis >& xAxisModel ) +{ + uno::Reference< beans::XPropertySet > xProp = + uno::Reference<beans::XPropertySet>::query( xAxisModel ); + if(xProp.is()) + { + try + { + xProp->getPropertyValue( C2U( "TextBreak" ) ) >>= this->bLineBreakAllowed; + xProp->getPropertyValue( C2U( "TextOverlap" ) ) >>= this->bOverlapAllowed; + xProp->getPropertyValue( C2U( "StackCharacters" ) ) >>= this->bStackCharacters; + xProp->getPropertyValue( C2U( "TextRotation" ) ) >>= this->fRotationAngleDegree; + + ::com::sun::star::chart::ChartAxisArrangeOrderType eArrangeOrder; + xProp->getPropertyValue( C2U( "ArrangeOrder" ) ) >>= eArrangeOrder; + switch(eArrangeOrder) + { + case ::com::sun::star::chart::ChartAxisArrangeOrderType_SIDE_BY_SIDE: + this->eStaggering = SIDE_BY_SIDE; + break; + case ::com::sun::star::chart::ChartAxisArrangeOrderType_STAGGER_EVEN: + this->eStaggering = STAGGER_EVEN; + break; + case ::com::sun::star::chart::ChartAxisArrangeOrderType_STAGGER_ODD: + this->eStaggering = STAGGER_ODD; + break; + default: + this->eStaggering = STAGGER_AUTO; + break; + } + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + } +} + +/* +sal_Int16 getSwappedWritingMode( sal_Int16 nWritingMode ) +{ + //LR_TB == LT + //RL_TB == RT (Arabic, Hebrew) + //TB_RL == TR (Japanese, Chinese, Korean) + // ?? TL (Mongolian) see also text::WritingMode2 + + switch(nWritingMode) + { + case text::WritingMode2::RL_TB: + return text::WritingMode2::TB_RL; + case text::WritingMode2::TB_RL: + return text::WritingMode2::RL_TB; + case text::WritingMode2::LR_TB: + return text::WritingMode2::TB_LR; + default: + return text::WritingMode2::LR_TB; + } +} +*/ + +sal_Bool AxisLabelProperties::getIsStaggered() const +{ + if( STAGGER_ODD == eStaggering || STAGGER_EVEN == eStaggering ) + return sal_True; + return sal_False; +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VAxisProperties.hxx b/chart2/source/view/axes/VAxisProperties.hxx new file mode 100644 index 000000000000..5b6bfe98f777 --- /dev/null +++ b/chart2/source/view/axes/VAxisProperties.hxx @@ -0,0 +1,168 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VAXIS_PROPERTIES_HXX +#define _CHART2_VAXIS_PROPERTIES_HXX + +#include "TickmarkProperties.hxx" +#include "PlottingPositionHelper.hxx" +#include "LabelAlignment.hxx" +#include "ExplicitCategoriesProvider.hxx" + +#include <com/sun/star/chart/ChartAxisLabelPosition.hpp> +#include <com/sun/star/chart/ChartAxisMarkPosition.hpp> +#include <com/sun/star/chart/ChartAxisPosition.hpp> +#include <com/sun/star/chart2/XAxis.hpp> +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/chart2/data/XTextualDataSequence.hpp> +#include <com/sun/star/awt/Rectangle.hpp> +#include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/drawing/TextVerticalAdjust.hpp> +#include <com/sun/star/drawing/TextHorizontalAdjust.hpp> +#include <com/sun/star/lang/Locale.hpp> + +#include <vector> + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +//These properties describe how a couple of labels are arranged one to another. +//The couple can contain all labels for all tickmark depth or just the labels for one single depth or +//the labels from an coherent range of tick depths (e.g. the major and first minor tickmarks should be handled together). +//... only allow side by side for different tick depth +enum AxisLabelStaggering +{ + SIDE_BY_SIDE + , STAGGER_EVEN + , STAGGER_ODD + , STAGGER_AUTO +}; + +struct AxisLabelProperties +{ + AxisLabelProperties(); + + ::com::sun::star::awt::Size m_aFontReferenceSize;//reference size to calculate the font height + ::com::sun::star::awt::Rectangle m_aMaximumSpaceForLabels;//Labels need to be clipped in order to fit into this rectangle + + sal_Int32 nNumberFormatKey; + + AxisLabelStaggering eStaggering; + + sal_Bool bLineBreakAllowed; + sal_Bool bOverlapAllowed; + + sal_Bool bStackCharacters; + double fRotationAngleDegree; + + sal_Int32 nRhythm; //show only each nth label with n==nRhythm + bool bRhythmIsFix; //states wether the given rhythm is fix or may be changed + + //methods: + void init( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XAxis >& xAxisModel ); + + sal_Bool getIsStaggered() const; +}; + +struct AxisProperties +{ + ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XAxis > m_xAxisModel; + + sal_Int32 m_nDimensionIndex; + bool m_bIsMainAxis;//not secondary axis + bool m_bSwapXAndY; + + ::com::sun::star::chart::ChartAxisPosition m_eCrossoverType; + ::com::sun::star::chart::ChartAxisLabelPosition m_eLabelPos; + ::com::sun::star::chart::ChartAxisMarkPosition m_eTickmarkPos; + + double* m_pfMainLinePositionAtOtherAxis; + double* m_pfExrtaLinePositionAtOtherAxis; + + bool m_bCrossingAxisHasReverseDirection; + bool m_bCrossingAxisIsCategoryAxes; + bool m_bAxisBetweenCategories; + + //this direction is used to indicate in which direction the labels are to be drawn + double m_fLabelDirectionSign; + //this direction is used to indicate in which direction inner tickmarks are to be drawn + double m_fInnerDirectionSign; + LabelAlignment m_aLabelAlignment; + sal_Bool m_bDisplayLabels; + + sal_Int32 m_nNumberFormatKey; + + /* + 0: no tickmarks 1: inner tickmarks + 2: outer tickmarks 3: inner and outer tickmarks + */ + sal_Int32 m_nMajorTickmarks; + sal_Int32 m_nMinorTickmarks; + ::std::vector<TickmarkProperties> m_aTickmarkPropertiesList; + + VLineProperties m_aLineProperties; + + //for category axes -> + sal_Int32 m_nAxisType;//REALNUMBER, CATEGORY etc. type ::com::sun::star::chart2::AxisType + bool m_bComplexCategories; + ExplicitCategoriesProvider* m_pExplicitCategoriesProvider;/*no ownership here*/ + ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::data::XTextualDataSequence > + m_xAxisTextProvider; //for categries or series names + //<- category axes + + //methods: + + AxisProperties( const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XAxis >& xAxisModel + , ExplicitCategoriesProvider* pExplicitCategoriesProvider ); + AxisProperties( const AxisProperties& rAxisProperties ); + virtual ~AxisProperties(); + virtual void init(bool bCartesian=false);//init from model data (m_xAxisModel) + + void initAxisPositioning( const ::com::sun::star::uno::Reference< ::com::sun::star::beans::XPropertySet >& xAxisProp ); + + static TickmarkProperties getBiggestTickmarkProperties(); + TickmarkProperties makeTickmarkPropertiesForComplexCategories( sal_Int32 nTickLength, sal_Int32 nTickStartDistanceToAxis, sal_Int32 nTextLevel ) const; + +private: + AxisProperties(); + +protected: + virtual TickmarkProperties makeTickmarkProperties( sal_Int32 nDepth ) const; + VLineProperties makeLinePropertiesForDepth( sal_Int32 nDepth ) const; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VCartesianAxis.cxx b/chart2/source/view/axes/VCartesianAxis.cxx new file mode 100644 index 000000000000..8ec895fbd452 --- /dev/null +++ b/chart2/source/view/axes/VCartesianAxis.cxx @@ -0,0 +1,1600 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include <basegfx/numeric/ftools.hxx> + +#include "VCartesianAxis.hxx" +#include "PlottingPositionHelper.hxx" +#include "ShapeFactory.hxx" +#include "CommonConverters.hxx" +#include "macros.hxx" +#include "ViewDefines.hxx" +#include "PropertyMapper.hxx" +#include "chartview/NumberFormatterWrapper.hxx" +#include "LabelPositionHelper.hxx" +#include "TrueGuard.hxx" +#include "BaseGFXHelper.hxx" +#include "AxisHelper.hxx" + +#include <rtl/math.hxx> +#include <tools/color.hxx> +#include <tools/debug.hxx> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <editeng/unoprnms.hxx> + +#include <algorithm> +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using namespace ::rtl::math; +using ::com::sun::star::uno::Reference; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +VCartesianAxis::VCartesianAxis( const AxisProperties& rAxisProperties + , const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , PlottingPositionHelper* pPosHelper )//takes ownership + : VAxisBase( nDimensionIndex, nDimensionCount, rAxisProperties, xNumberFormatsSupplier ) +{ + if( pPosHelper ) + m_pPosHelper = pPosHelper; + else + m_pPosHelper = new PlottingPositionHelper(); +} + +VCartesianAxis::~VCartesianAxis() +{ + delete m_pPosHelper; + m_pPosHelper = NULL; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- + +Reference< drawing::XShape > createSingleLabel( + const Reference< lang::XMultiServiceFactory>& xShapeFactory + , const Reference< drawing::XShapes >& xTarget + , const awt::Point& rAnchorScreenPosition2D + , const rtl::OUString& rLabel + , const AxisLabelProperties& rAxisLabelProperties + , const AxisProperties& rAxisProperties + , const tNameSequence& rPropNames + , const tAnySequence& rPropValues + ) +{ + if(!rLabel.getLength()) + return 0; + + // #i78696# use mathematically correct rotation now + const double fRotationAnglePi(rAxisLabelProperties.fRotationAngleDegree * (F_PI / -180.0)); + uno::Any aATransformation = ShapeFactory::makeTransformation( rAnchorScreenPosition2D, fRotationAnglePi ); + rtl::OUString aLabel = ShapeFactory::getStackedString( rLabel, rAxisLabelProperties.bStackCharacters ); + + Reference< drawing::XShape > xShape2DText = ShapeFactory(xShapeFactory) + .createText( xTarget, aLabel, rPropNames, rPropValues, aATransformation ); + + //correctPositionForRotation + LabelPositionHelper::correctPositionForRotation( xShape2DText + , rAxisProperties.m_aLabelAlignment, rAxisLabelProperties.fRotationAngleDegree, false ); + + return xShape2DText; +} + +bool lcl_doesShapeOverlapWithTickmark( const Reference< drawing::XShape >& xShape + , double fRotationAngleDegree + , const basegfx::B2DVector& rTickScreenPosition + , bool bIsHorizontalAxis, bool bIsVerticalAxis ) +{ + if(!xShape.is()) + return false; + + ::basegfx::B2IRectangle aShapeRect = BaseGFXHelper::makeRectangle(xShape->getPosition(),ShapeFactory::getSizeAfterRotation( xShape, fRotationAngleDegree )); + + if( bIsVerticalAxis ) + { + return ( (rTickScreenPosition.getY() >= aShapeRect.getMinY()) + && (rTickScreenPosition.getY() <= aShapeRect.getMaxY()) ); + } + if( bIsHorizontalAxis ) + { + return ( (rTickScreenPosition.getX() >= aShapeRect.getMinX()) + && (rTickScreenPosition.getX() <= aShapeRect.getMaxX()) ); + } + + basegfx::B2IVector aPosition( + static_cast<sal_Int32>( rTickScreenPosition.getX() ) + , static_cast<sal_Int32>( rTickScreenPosition.getY() ) ); + return aShapeRect.isInside(aPosition); +} + +bool doesOverlap( const Reference< drawing::XShape >& xShape1 + , const Reference< drawing::XShape >& xShape2 + , double fRotationAngleDegree ) +{ + if( !xShape1.is() || !xShape2.is() ) + return false; + + ::basegfx::B2IRectangle aRect1( BaseGFXHelper::makeRectangle(xShape1->getPosition(),ShapeFactory::getSizeAfterRotation( xShape1, fRotationAngleDegree ))); + ::basegfx::B2IRectangle aRect2( BaseGFXHelper::makeRectangle(xShape2->getPosition(),ShapeFactory::getSizeAfterRotation( xShape2, fRotationAngleDegree ))); + return aRect1.overlaps(aRect2); +} + +void removeShapesAtWrongRhythm( TickIter& rIter + , sal_Int32 nCorrectRhythm + , sal_Int32 nMaxTickToCheck + , const Reference< drawing::XShapes >& xTarget ) +{ + sal_Int32 nTick = 0; + for( TickInfo* pTickInfo = rIter.firstInfo() + ; pTickInfo && nTick <= nMaxTickToCheck + ; pTickInfo = rIter.nextInfo(), nTick++ ) + { + //remove labels which does not fit into the rhythm + if( nTick%nCorrectRhythm != 0) + { + if(pTickInfo->xTextShape.is()) + { + xTarget->remove(pTickInfo->xTextShape); + pTickInfo->xTextShape = NULL; + } + } + } +} + +class EquidistantLabelIterator : public EquidistantTickIter +{ + //this Iterator iterates over existing text labels + + //if the labels are staggered and bInnerLine is true + //we iterate only through the labels which are lying more inside the diagram + + //if the labels are staggered and bInnerLine is false + //we iterate only through the labels which are lying more outside the diagram + + //if the labels are not staggered + //we iterate through all labels + +public: + EquidistantLabelIterator( ::std::vector< ::std::vector< TickInfo > >& rTickInfos + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , const AxisLabelStaggering eAxisLabelStaggering + , bool bInnerLine + , sal_Int32 nMinDepth=0, sal_Int32 nMaxDepth=-1 ); + + virtual TickInfo* firstInfo(); + virtual TickInfo* nextInfo(); + +private: //methods + EquidistantLabelIterator(); + +private: //member + const AxisLabelStaggering m_eAxisLabelStaggering; + bool m_bInnerLine; +}; + +EquidistantLabelIterator::EquidistantLabelIterator( ::std::vector< ::std::vector< TickInfo > >& rTickInfos + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , const AxisLabelStaggering eAxisLabelStaggering + , bool bInnerLine + , sal_Int32 nMinDepth, sal_Int32 nMaxDepth ) + : EquidistantTickIter( rTickInfos, rIncrement, nMinDepth, nMaxDepth ) + , m_eAxisLabelStaggering(eAxisLabelStaggering) + , m_bInnerLine(bInnerLine) +{ +} + +TickInfo* EquidistantLabelIterator::firstInfo() +{ + TickInfo* pTickInfo = EquidistantTickIter::firstInfo(); + while( pTickInfo && !pTickInfo->xTextShape.is() ) + pTickInfo = EquidistantTickIter::nextInfo(); + if(!pTickInfo) + return NULL; + if( (STAGGER_EVEN==m_eAxisLabelStaggering && m_bInnerLine) + || + (STAGGER_ODD==m_eAxisLabelStaggering && !m_bInnerLine) + ) + { + //skip first label + do + pTickInfo = EquidistantTickIter::nextInfo(); + while( pTickInfo && !pTickInfo->xTextShape.is() ); + } + if(!pTickInfo) + return NULL; + return pTickInfo; +} + +TickInfo* EquidistantLabelIterator::nextInfo() +{ + TickInfo* pTickInfo = NULL; + //get next label + do + pTickInfo = EquidistantTickIter::nextInfo(); + while( pTickInfo && !pTickInfo->xTextShape.is() ); + + if( STAGGER_EVEN==m_eAxisLabelStaggering + || STAGGER_ODD==m_eAxisLabelStaggering ) + { + //skip one label + do + pTickInfo = EquidistantTickIter::nextInfo(); + while( pTickInfo && !pTickInfo->xTextShape.is() ); + } + return pTickInfo; +} + +B2DVector lcl_getLabelsDistance( TickIter& rIter, const B2DVector& rDistanceTickToText ) +{ + //calculates the height or width of a line of labels + //thus a following line of labels can be shifted for that distance + + B2DVector aRet(0,0); + + sal_Int32 nDistanceTickToText = static_cast<sal_Int32>( rDistanceTickToText.getLength() ); + if( nDistanceTickToText==0.0) + return aRet; + + B2DVector aStaggerDirection(rDistanceTickToText); + aStaggerDirection.normalize(); + + sal_Int32 nDistance=0; + Reference< drawing::XShape > xShape2DText(NULL); + for( TickInfo* pTickInfo = rIter.firstInfo() + ; pTickInfo + ; pTickInfo = rIter.nextInfo() ) + { + xShape2DText = pTickInfo->xTextShape; + if( xShape2DText.is() ) + { + awt::Size aSize = xShape2DText->getSize(); + if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY())) + nDistance = ::std::max(nDistance,aSize.Width); + else + nDistance = ::std::max(nDistance,aSize.Height); + } + } + + aRet = aStaggerDirection*nDistance; + + //add extra distance for vertical distance + if(fabs(aStaggerDirection.getX())>fabs(aStaggerDirection.getY())) + aRet += rDistanceTickToText; + + return aRet; +} + +void lcl_shiftLables( TickIter& rIter, const B2DVector& rStaggerDistance ) +{ + if(rStaggerDistance.getLength()==0.0) + return; + Reference< drawing::XShape > xShape2DText(NULL); + for( TickInfo* pTickInfo = rIter.firstInfo() + ; pTickInfo + ; pTickInfo = rIter.nextInfo() ) + { + xShape2DText = pTickInfo->xTextShape; + if( xShape2DText.is() ) + { + awt::Point aPos = xShape2DText->getPosition(); + aPos.X += static_cast<sal_Int32>(rStaggerDistance.getX()); + aPos.Y += static_cast<sal_Int32>(rStaggerDistance.getY()); + xShape2DText->setPosition( aPos ); + } + } +} + +class MaxLabelEquidistantTickIter : public EquidistantTickIter +{ + //iterate over first two and last two labels and the longest label +public: + MaxLabelEquidistantTickIter( ::std::vector< ::std::vector< TickInfo > >& rTickInfos + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , sal_Int32 nLongestLabelIndex ); + virtual ~MaxLabelEquidistantTickIter(); + + virtual TickInfo* nextInfo(); + +private: + sal_Int32 m_nLongestLabelIndex; +}; + +MaxLabelEquidistantTickIter::MaxLabelEquidistantTickIter( ::std::vector< ::std::vector< TickInfo > >& rTickInfos + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , sal_Int32 nLongestLabelIndex ) + : EquidistantTickIter( rTickInfos, rIncrement, 0//nMinDepth + , 0//nMaxDepth + ) + , m_nLongestLabelIndex( nLongestLabelIndex ) +{ + sal_Int32 nMaxIndex = getMaxIndex(); + + //ensure correct value: + if( m_nLongestLabelIndex<0 || m_nLongestLabelIndex>nMaxIndex) + m_nLongestLabelIndex = 0; + + //last label is checked anyhow + if( m_nLongestLabelIndex==nMaxIndex ) + m_nLongestLabelIndex = 0; + //label before last is checked anyhow + if( m_nLongestLabelIndex+1==nMaxIndex ) + m_nLongestLabelIndex = 0; +} +MaxLabelEquidistantTickIter::~MaxLabelEquidistantTickIter() +{ +} + +TickInfo* MaxLabelEquidistantTickIter::nextInfo() +{ + sal_Int32 nCurrentPos = getCurrentIndex(); + sal_Int32 nMaxIndex = getMaxIndex(); + if( nCurrentPos>0 ) + { + if( m_nLongestLabelIndex>1 && nCurrentPos<m_nLongestLabelIndex ) + gotoIndex( m_nLongestLabelIndex-1 ) ; + else + { + if( nMaxIndex>3 && nCurrentPos<nMaxIndex-1 ) + gotoIndex( nMaxIndex-2 ); + else if( nMaxIndex>2 && nCurrentPos<nMaxIndex ) + gotoIndex( nMaxIndex-1 ); + } + } + + return EquidistantTickIter::nextInfo(); +} + +bool VCartesianAxis::isBreakOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties + , bool bIsHorizontalAxis ) +{ + if( m_aTextLabels.getLength() > 100 ) + return false; + if( !rAxisLabelProperties.bLineBreakAllowed ) + return false; + if( rAxisLabelProperties.bStackCharacters ) + return false; + //no break for value axis + if( !m_bUseTextLabels ) + return false; + if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) ) + return false; + //break only for horizontal axis + return bIsHorizontalAxis; +} + +bool VCartesianAxis::isAutoStaggeringOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties + , bool bIsHorizontalAxis, bool bIsVerticalAxis ) +{ + if( rAxisLabelProperties.eStaggering != STAGGER_AUTO ) + return false; + if( rAxisLabelProperties.bOverlapAllowed ) + return false; + if( rAxisLabelProperties.bLineBreakAllowed ) //auto line break or auto staggering, doing both automatisms they may conflict... + return false; + if( !::rtl::math::approxEqual( rAxisLabelProperties.fRotationAngleDegree, 0.0 ) ) + return false; + //automatic staggering only for horizontal axis with horizontal text + //or vertical axis with vertical text + if( bIsHorizontalAxis ) + return !rAxisLabelProperties.bStackCharacters; + if( bIsVerticalAxis ) + return rAxisLabelProperties.bStackCharacters; + return false; +} + +struct ComplexCategoryPlacement +{ + rtl::OUString Text; + sal_Int32 Count; + double TickValue; + + ComplexCategoryPlacement( const rtl::OUString& rText, sal_Int32 nCount, double fTickValue ) + : Text(rText), Count(nCount), TickValue(fTickValue) + {} +}; + +void VCartesianAxis::createAllTickInfosFromComplexCategories( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos, bool bShiftedPosition ) +{ + //no minor tickmarks will be generated! + //order is: inner labels first , outer labels last (that is different to all other TickIter cases) + if(!bShiftedPosition) + { + rAllTickInfos.clear(); + sal_Int32 nLevel=0; + sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); + for( ; nLevel<nLevelCount; nLevel++ ) + { + ::std::vector< TickInfo > aTickInfoVector; + std::vector< ComplexCategory > aComplexCategories( m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel( nLevel ) ); + sal_Int32 nCatIndex = 0; + std::vector< ComplexCategory >::const_iterator aIt(aComplexCategories.begin()); + std::vector< ComplexCategory >::const_iterator aEnd(aComplexCategories.end()); + for(;aIt!=aEnd;++aIt) + { + TickInfo aTickInfo; + ComplexCategory aCat(*aIt); + sal_Int32 nCount = aCat.Count; + if( nCatIndex + 0.5 + nCount >= m_aScale.Maximum ) + { + nCount = static_cast<sal_Int32>(m_aScale.Maximum - 0.5 - nCatIndex); + if( nCount <= 0 ) + nCount = 1; + } + aTickInfo.fScaledTickValue = nCatIndex + 0.5 + nCount/2.0; + aTickInfo.nFactorForLimitedTextWidth = nCount; + aTickInfo.aText = aCat.Text; + aTickInfoVector.push_back(aTickInfo); + nCatIndex += nCount; + if( nCatIndex + 0.5 >= m_aScale.Maximum ) + break; + } + rAllTickInfos.push_back(aTickInfoVector); + } + } + else //bShiftedPosition==true + { + rAllTickInfos.clear(); + sal_Int32 nLevel=0; + sal_Int32 nLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); + for( ; nLevel<nLevelCount; nLevel++ ) + { + ::std::vector< TickInfo > aTickInfoVector; + std::vector< ComplexCategory > aComplexCategories( m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoriesByLevel( nLevel ) ); + sal_Int32 nCatIndex = 0; + std::vector< ComplexCategory >::const_iterator aIt(aComplexCategories.begin()); + std::vector< ComplexCategory >::const_iterator aEnd(aComplexCategories.end()); + for(;aIt!=aEnd;++aIt) + { + TickInfo aTickInfo; + ComplexCategory aCat(*aIt); + aTickInfo.fScaledTickValue = nCatIndex + 0.5; + aTickInfoVector.push_back(aTickInfo); + nCatIndex += aCat.Count; + if( nCatIndex + 0.5 > m_aScale.Maximum ) + break; + } + //fill up with single ticks until maximum scale + while( nCatIndex + 0.5 < m_aScale.Maximum ) + { + TickInfo aTickInfo; + aTickInfo.fScaledTickValue = nCatIndex + 0.5; + aTickInfoVector.push_back(aTickInfo); + nCatIndex ++; + if( nLevel>0 ) + break; + } + //add an additional tick at the end + { + TickInfo aTickInfo; + aTickInfo.fScaledTickValue = m_aScale.Maximum; + aTickInfoVector.push_back(aTickInfo); + } + rAllTickInfos.push_back(aTickInfoVector); + } + } +} + +void VCartesianAxis::createAllTickInfos( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) +{ + if( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) + createAllTickInfosFromComplexCategories( rAllTickInfos, false ); + else + VAxisBase::createAllTickInfos(rAllTickInfos); +} + +::std::auto_ptr< TickIter > VCartesianAxis::createLabelTickIterator( sal_Int32 nTextLevel ) +{ + if( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) + { + if( nTextLevel>=0 && nTextLevel < static_cast< sal_Int32 >(m_aAllTickInfos.size()) ) + return ::std::auto_ptr< TickIter >( new PureTickIter( m_aAllTickInfos[nTextLevel] ) ); + } + else + { + if(nTextLevel==0) + return ::std::auto_ptr< TickIter >( new EquidistantTickIter( m_aAllTickInfos, m_aIncrement, 0, 0 ) ); + } + return ::std::auto_ptr< TickIter >(); +} +::std::auto_ptr< TickIter > VCartesianAxis::createMaximumLabelTickIterator( sal_Int32 nTextLevel ) +{ + if( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) + { + return createLabelTickIterator( nTextLevel ); //mmmm maybe todo: create less than all texts here + } + else + { + if(nTextLevel==0) + { + sal_Int32 nLongestLabelIndex = m_bUseTextLabels ? this->getIndexOfLongestLabel( m_aTextLabels ) : 0; + return ::std::auto_ptr< TickIter >( new MaxLabelEquidistantTickIter( m_aAllTickInfos, m_aIncrement, nLongestLabelIndex ) ); + } + } + return ::std::auto_ptr< TickIter >(); +} + +sal_Int32 VCartesianAxis::getTextLevelCount() const +{ + sal_Int32 nTextLevelCount = 1; + if( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) + nTextLevelCount = m_aAxisProperties.m_pExplicitCategoriesProvider->getCategoryLevelCount(); + return nTextLevelCount; +} + +bool VCartesianAxis::createTextShapes( + const Reference< drawing::XShapes >& xTarget + , TickIter& rTickIter + , AxisLabelProperties& rAxisLabelProperties + , TickmarkHelper_2D* pTickmarkHelper + , sal_Int32 nScreenDistanceBetweenTicks ) +{ + //returns true if the text shapes have been created succesfully + //otherwise false - in this case the AxisLabelProperties have changed + //and contain new instructions for the next try for text shape creation + + Reference< XScaling > xInverseScaling( NULL ); + if( m_aScale.Scaling.is() ) + xInverseScaling = m_aScale.Scaling->getInverseScaling(); + + FixedNumberFormatter aFixedNumberFormatter( + m_xNumberFormatsSupplier, rAxisLabelProperties.nNumberFormatKey ); + + const bool bIsHorizontalAxis = pTickmarkHelper->isHorizontalAxis(); + const bool bIsVerticalAxis = pTickmarkHelper->isVerticalAxis(); + bool bIsStaggered = rAxisLabelProperties.getIsStaggered(); + B2DVector aTextToTickDistance( pTickmarkHelper->getDistanceAxisTickToText( m_aAxisProperties, true ) ); + sal_Int32 nLimitedSpaceForText = -1; + if( isBreakOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis ) ) + { + nLimitedSpaceForText = nScreenDistanceBetweenTicks; + if( bIsStaggered ) + nLimitedSpaceForText *= 2; + + if( nLimitedSpaceForText > 0 ) + { //reduce space for a small amount to have a visible distance between the labels: + sal_Int32 nReduce = (nLimitedSpaceForText*5)/100; + if(!nReduce) + nReduce = 1; + nLimitedSpaceForText -= nReduce; + } + } + + std::vector< ComplexCategoryPlacement > aComplexCategoryPlacements; + uno::Sequence< rtl::OUString >* pCategories = 0; + if( m_bUseTextLabels && !m_aAxisProperties.m_bComplexCategories ) + pCategories = &m_aTextLabels; + + TickInfo* pPreviousVisibleTickInfo = NULL; + TickInfo* pPREPreviousVisibleTickInfo = NULL; + TickInfo* pLastVisibleNeighbourTickInfo = NULL; + + //------------------------------------------------ + //prepare properties for multipropertyset-interface of shape + tNameSequence aPropNames; + tAnySequence aPropValues; + + bool bLimitedHeight = fabs(aTextToTickDistance.getX()) > fabs(aTextToTickDistance.getY()); + Reference< beans::XPropertySet > xProps( m_aAxisProperties.m_xAxisModel, uno::UNO_QUERY ); + PropertyMapper::getTextLabelMultiPropertyLists( xProps, aPropNames, aPropValues, false + , nLimitedSpaceForText, bLimitedHeight ); + LabelPositionHelper::doDynamicFontResize( aPropValues, aPropNames, xProps + , m_aAxisLabelProperties.m_aFontReferenceSize ); + LabelPositionHelper::changeTextAdjustment( aPropValues, aPropNames, m_aAxisProperties.m_aLabelAlignment ); + + uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,C2U("CharColor")); + sal_Int32 nColor = Color( COL_AUTO ).GetColor(); + if(pColorAny) + *pColorAny >>= nColor; + + uno::Any* pLimitedSpaceAny = PropertyMapper::getValuePointerForLimitedSpace(aPropValues,aPropNames,bLimitedHeight); + //------------------------------------------------ + + sal_Int32 nTick = 0; + for( TickInfo* pTickInfo = rTickIter.firstInfo() + ; pTickInfo + ; pTickInfo = rTickIter.nextInfo(), nTick++ ) + { + pLastVisibleNeighbourTickInfo = bIsStaggered ? + pPREPreviousVisibleTickInfo : pPreviousVisibleTickInfo; + + //don't create labels which does not fit into the rhythm + if( nTick%rAxisLabelProperties.nRhythm != 0) + continue; + + //don't create labels for invisible ticks + if( !pTickInfo->bPaintIt ) + continue; + + //if NO OVERLAP -> don't create labels where the tick overlaps + //with the text of the last neighbour tickmark + if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed ) + { + if( lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape + , rAxisLabelProperties.fRotationAngleDegree + , pTickInfo->aTickScreenPosition + , bIsHorizontalAxis, bIsVerticalAxis ) ) + { + bool bOverlapAlsoAfterSwitchingOnAutoStaggering = true; + if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) ) + { + bIsStaggered = true; + rAxisLabelProperties.eStaggering = STAGGER_EVEN; + pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo; + if( !pLastVisibleNeighbourTickInfo || + !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape + , rAxisLabelProperties.fRotationAngleDegree + , pTickInfo->aTickScreenPosition + , bIsHorizontalAxis, bIsVerticalAxis ) ) + bOverlapAlsoAfterSwitchingOnAutoStaggering = false; + } + if( bOverlapAlsoAfterSwitchingOnAutoStaggering ) + { + if( rAxisLabelProperties.bRhythmIsFix ) + continue; + rAxisLabelProperties.nRhythm++; + removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget ); + return false; + } + } + } + + pTickInfo->updateUnscaledValue( xInverseScaling ); + + bool bHasExtraColor=false; + sal_Int32 nExtraColor=0; + + rtl::OUString aLabel; + if(pCategories) + { + sal_Int32 nIndex = static_cast< sal_Int32 >(pTickInfo->fUnscaledTickValue) - 1; //first category (index 0) matches with real number 1.0 + if( nIndex>=0 && nIndex<pCategories->getLength() ) + aLabel = (*pCategories)[nIndex]; + } + else if( m_aAxisProperties.m_bComplexCategories ) + { + aLabel = pTickInfo->aText; + } + else + aLabel = aFixedNumberFormatter.getFormattedString( pTickInfo->fUnscaledTickValue, nExtraColor, bHasExtraColor ); + + if(pColorAny) + *pColorAny = uno::makeAny(bHasExtraColor?nExtraColor:nColor); + if(pLimitedSpaceAny) + *pLimitedSpaceAny = uno::makeAny(sal_Int32(nLimitedSpaceForText*pTickInfo->nFactorForLimitedTextWidth)); + + B2DVector aTickScreenPos2D( pTickInfo->aTickScreenPosition ); + aTickScreenPos2D += aTextToTickDistance; + awt::Point aAnchorScreenPosition2D( + static_cast<sal_Int32>(aTickScreenPos2D.getX()) + ,static_cast<sal_Int32>(aTickScreenPos2D.getY())); + + //create single label + if(!pTickInfo->xTextShape.is()) + pTickInfo->xTextShape = createSingleLabel( m_xShapeFactory, xTarget + , aAnchorScreenPosition2D, aLabel + , rAxisLabelProperties, m_aAxisProperties + , aPropNames, aPropValues ); + if(!pTickInfo->xTextShape.is()) + continue; + + recordMaximumTextSize( pTickInfo->xTextShape, rAxisLabelProperties.fRotationAngleDegree ); + + //if NO OVERLAP -> remove overlapping shapes + if( pLastVisibleNeighbourTickInfo && !rAxisLabelProperties.bOverlapAllowed ) + { + if( doesOverlap( pLastVisibleNeighbourTickInfo->xTextShape, pTickInfo->xTextShape, m_aAxisLabelProperties.fRotationAngleDegree ) ) + { + bool bOverlapAlsoAfterSwitchingOnAutoStaggering = true; + if( !bIsStaggered && isAutoStaggeringOfLabelsAllowed( rAxisLabelProperties, bIsHorizontalAxis, bIsVerticalAxis ) ) + { + bIsStaggered = true; + rAxisLabelProperties.eStaggering = STAGGER_EVEN; + pLastVisibleNeighbourTickInfo = pPREPreviousVisibleTickInfo; + if( !pLastVisibleNeighbourTickInfo || + !lcl_doesShapeOverlapWithTickmark( pLastVisibleNeighbourTickInfo->xTextShape + , rAxisLabelProperties.fRotationAngleDegree + , pTickInfo->aTickScreenPosition + , bIsHorizontalAxis, bIsVerticalAxis ) ) + bOverlapAlsoAfterSwitchingOnAutoStaggering = false; + } + if( bOverlapAlsoAfterSwitchingOnAutoStaggering ) + { + if( rAxisLabelProperties.bRhythmIsFix ) + { + xTarget->remove(pTickInfo->xTextShape); + pTickInfo->xTextShape = NULL; + continue; + } + rAxisLabelProperties.nRhythm++; + removeShapesAtWrongRhythm( rTickIter, rAxisLabelProperties.nRhythm, nTick, xTarget ); + return false; + } + } + } + + pPREPreviousVisibleTickInfo = pPreviousVisibleTickInfo; + pPreviousVisibleTickInfo = pTickInfo; + } + return true; +} + +drawing::PointSequenceSequence lcl_makePointSequence( B2DVector& rStart, B2DVector& rEnd ) +{ + drawing::PointSequenceSequence aPoints(1); + aPoints[0].realloc(2); + aPoints[0][0].X = static_cast<sal_Int32>(rStart.getX()); + aPoints[0][0].Y = static_cast<sal_Int32>(rStart.getY()); + aPoints[0][1].X = static_cast<sal_Int32>(rEnd.getX()); + aPoints[0][1].Y = static_cast<sal_Int32>(rEnd.getY()); + return aPoints; +} + +double VCartesianAxis::getLogicValueWhereMainLineCrossesOtherAxis() const +{ + double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); + double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); + + double fCrossesOtherAxis; + if(m_aAxisProperties.m_pfMainLinePositionAtOtherAxis) + fCrossesOtherAxis = *m_aAxisProperties.m_pfMainLinePositionAtOtherAxis; + else + { + if( ::com::sun::star::chart::ChartAxisPosition_END == m_aAxisProperties.m_eCrossoverType ) + fCrossesOtherAxis = fMax; + else + fCrossesOtherAxis = fMin; + } + return fCrossesOtherAxis; +} + +double VCartesianAxis::getLogicValueWhereLabelLineCrossesOtherAxis() const +{ + double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); + double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); + + double fCrossesOtherAxis; + if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START == m_aAxisProperties.m_eLabelPos ) + fCrossesOtherAxis = fMin; + else if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END == m_aAxisProperties.m_eLabelPos ) + fCrossesOtherAxis = fMax; + else + fCrossesOtherAxis = getLogicValueWhereMainLineCrossesOtherAxis(); + return fCrossesOtherAxis; +} + +bool VCartesianAxis::getLogicValueWhereExtraLineCrossesOtherAxis( double& fCrossesOtherAxis ) const +{ + if( !m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis ) + return false; + double fMin = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMinY(); + double fMax = (m_nDimensionIndex==1) ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMaxY(); + if( *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis <= fMin + || *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis >= fMax ) + return false; + fCrossesOtherAxis = *m_aAxisProperties.m_pfExrtaLinePositionAtOtherAxis; + return true; +} + +B2DVector VCartesianAxis::getScreenPosition( double fLogicX, double fLogicY, double fLogicZ ) const +{ + B2DVector aRet(0,0); + + if( m_pPosHelper ) + { + drawing::Position3D aScenePos = m_pPosHelper->transformLogicToScene( fLogicX, fLogicY, fLogicZ, true ); + if(3==m_nDimension) + { + if( m_xLogicTarget.is() && m_pPosHelper && m_pShapeFactory ) + { + tPropertyNameMap aDummyPropertyNameMap; + Reference< drawing::XShape > xShape3DAnchor = m_pShapeFactory->createCube( m_xLogicTarget + , aScenePos,drawing::Direction3D(1,1,1), 0, 0, aDummyPropertyNameMap); + awt::Point a2DPos = xShape3DAnchor->getPosition(); //get 2D position from xShape3DAnchor + m_xLogicTarget->remove(xShape3DAnchor); + aRet.setX( a2DPos.X ); + aRet.setY( a2DPos.Y ); + } + else + { + DBG_ERROR("cannot calculate scrren position in VCartesianAxis::getScreenPosition"); + } + } + else + { + aRet.setX( aScenePos.PositionX ); + aRet.setY( aScenePos.PositionY ); + } + } + + return aRet; +} + +VCartesianAxis::ScreenPosAndLogicPos VCartesianAxis::getScreenPosAndLogicPos( double fLogicX_, double fLogicY_, double fLogicZ_ ) const +{ + ScreenPosAndLogicPos aRet; + aRet.fLogicX = fLogicX_; + aRet.fLogicY = fLogicY_; + aRet.fLogicZ = fLogicZ_; + aRet.aScreenPos = getScreenPosition( fLogicX_, fLogicY_, fLogicZ_ ); + return aRet; +} + +typedef ::std::vector< VCartesianAxis::ScreenPosAndLogicPos > tScreenPosAndLogicPosList; +struct lcl_LessXPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool > +{ + inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 ) + { + return ( rPos1.aScreenPos.getX() < rPos2.aScreenPos.getX() ); + } +}; + +struct lcl_GreaterYPos : ::std::binary_function< VCartesianAxis::ScreenPosAndLogicPos, VCartesianAxis::ScreenPosAndLogicPos, bool > +{ + inline bool operator() ( const VCartesianAxis::ScreenPosAndLogicPos& rPos1, const VCartesianAxis::ScreenPosAndLogicPos& rPos2 ) + { + return ( rPos1.aScreenPos.getY() > rPos2.aScreenPos.getY() ); + } +}; + +void VCartesianAxis::get2DAxisMainLine( B2DVector& rStart, B2DVector& rEnd, double fCrossesOtherAxis ) +{ + //m_aAxisProperties might get updated and changed here because + // the label alignmant and inner direction sign depends exactly of the choice of the axis line position which is made here in this method + + double fMinX = m_pPosHelper->getLogicMinX(); + double fMinY = m_pPosHelper->getLogicMinY(); + double fMinZ = m_pPosHelper->getLogicMinZ(); + double fMaxX = m_pPosHelper->getLogicMaxX(); + double fMaxY = m_pPosHelper->getLogicMaxY(); + double fMaxZ = m_pPosHelper->getLogicMaxZ(); + + double fXStart = fMinX; + double fYStart = fMinY; + double fZStart = fMinZ; + double fXEnd = fXStart; + double fYEnd = fYStart; + double fZEnd = fZStart; + + double fXOnXPlane = fMinX; + double fXOther = fMaxX; + int nDifferentValue = !m_pPosHelper->isMathematicalOrientationX() ? -1 : 1; + if( !m_pPosHelper->isSwapXAndY() ) + nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1; + else + nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1; + if( nDifferentValue<0 ) + { + fXOnXPlane = fMaxX; + fXOther = fMinX; + } + + double fYOnYPlane = fMinY; + double fYOther = fMaxY; + nDifferentValue = !m_pPosHelper->isMathematicalOrientationY() ? -1 : 1; + if( !m_pPosHelper->isSwapXAndY() ) + nDifferentValue *= (CuboidPlanePosition_Bottom != m_eBottomPos) ? -1 : 1; + else + nDifferentValue *= (CuboidPlanePosition_Left != m_eLeftWallPos) ? -1 : 1; + if( nDifferentValue<0 ) + { + fYOnYPlane = fMaxY; + fYOther = fMinY; + } + + double fZOnZPlane = fMaxZ; + double fZOther = fMinZ; + nDifferentValue = !m_pPosHelper->isMathematicalOrientationZ() ? -1 : 1; + nDifferentValue *= (CuboidPlanePosition_Back != m_eBackWallPos) ? -1 : 1; + if( nDifferentValue<0 ) + { + fZOnZPlane = fMinZ; + fZOther = fMaxZ; + } + + if( 0==m_nDimensionIndex ) //x-axis + { + if( fCrossesOtherAxis < fMinY ) + fCrossesOtherAxis = fMinY; + else if( fCrossesOtherAxis > fMaxY ) + fCrossesOtherAxis = fMaxY; + + fYStart = fYEnd = fCrossesOtherAxis; + fXEnd=m_pPosHelper->getLogicMaxX(); + + if(3==m_nDimension) + { + if( AxisHelper::isAxisPositioningEnabled() ) + { + if( ::rtl::math::approxEqual( fYOther, fYStart) ) + fZStart = fZEnd = fZOnZPlane; + else + fZStart = fZEnd = fZOther; + } + else + { + rStart = getScreenPosition( fXStart, fYStart, fZStart ); + rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); + + double fDeltaX = rEnd.getX() - rStart.getX(); + double fDeltaY = rEnd.getY() - rStart.getY(); + + //only those points are candidates which are lying on exactly one wall as these are outer edges + tScreenPosAndLogicPosList aPosList; + aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOnYPlane, fZOther ) ); + aPosList.push_back( getScreenPosAndLogicPos( fMinX, fYOther, fZOnZPlane ) ); + + if( fabs(fDeltaY) > fabs(fDeltaX) ) + { + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; + //choose most left positions + ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() ); + m_aAxisProperties.m_fLabelDirectionSign = fDeltaY<0 ? -1 : 1; + } + else + { + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; + //choose most bottom positions + ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); + m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; + } + ScreenPosAndLogicPos aBestPos( aPosList[0] ); + fYStart = fYEnd = aBestPos.fLogicY; + fZStart = fZEnd = aBestPos.fLogicZ; + if( !m_pPosHelper->isMathematicalOrientationX() ) + m_aAxisProperties.m_fLabelDirectionSign *= -1; + } + }//end 3D x axis + } + else if( 1==m_nDimensionIndex ) //y-axis + { + if( fCrossesOtherAxis < fMinX ) + fCrossesOtherAxis = fMinX; + else if( fCrossesOtherAxis > fMaxX ) + fCrossesOtherAxis = fMaxX; + + fXStart = fXEnd = fCrossesOtherAxis; + fYEnd=m_pPosHelper->getLogicMaxY(); + + if(3==m_nDimension) + { + if( AxisHelper::isAxisPositioningEnabled() ) + { + if( ::rtl::math::approxEqual( fXOther, fXStart) ) + fZStart = fZEnd = fZOnZPlane; + else + fZStart = fZEnd = fZOther; + } + else + { + rStart = getScreenPosition( fXStart, fYStart, fZStart ); + rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); + + double fDeltaX = rEnd.getX() - rStart.getX(); + double fDeltaY = rEnd.getY() - rStart.getY(); + + //only those points are candidates which are lying on exactly one wall as these are outer edges + tScreenPosAndLogicPosList aPosList; + aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fMinY, fZOther ) ); + aPosList.push_back( getScreenPosAndLogicPos( fXOther, fMinY, fZOnZPlane ) ); + + if( fabs(fDeltaY) > fabs(fDeltaX) ) + { + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; + //choose most left positions + ::std::sort( aPosList.begin(), aPosList.end(), lcl_LessXPos() ); + m_aAxisProperties.m_fLabelDirectionSign = fDeltaY<0 ? -1 : 1; + } + else + { + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; + //choose most bottom positions + ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); + m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; + } + ScreenPosAndLogicPos aBestPos( aPosList[0] ); + fXStart = fXEnd = aBestPos.fLogicX; + fZStart = fZEnd = aBestPos.fLogicZ; + if( !m_pPosHelper->isMathematicalOrientationY() ) + m_aAxisProperties.m_fLabelDirectionSign *= -1; + } + }//end 3D y axis + } + else //z-axis + { + fZEnd = m_pPosHelper->getLogicMaxZ(); + if( AxisHelper::isAxisPositioningEnabled() ) + { + if( !m_aAxisProperties.m_bSwapXAndY ) + { + if( fCrossesOtherAxis < fMinY ) + fCrossesOtherAxis = fMinY; + else if( fCrossesOtherAxis > fMaxY ) + fCrossesOtherAxis = fMaxY; + fYStart = fYEnd = fCrossesOtherAxis; + + if( ::rtl::math::approxEqual( fYOther, fYStart) ) + fXStart = fXEnd = fXOnXPlane; + else + fXStart = fXEnd = fXOther; + } + else + { + if( fCrossesOtherAxis < fMinX ) + fCrossesOtherAxis = fMinX; + else if( fCrossesOtherAxis > fMaxX ) + fCrossesOtherAxis = fMaxX; + fXStart = fXEnd = fCrossesOtherAxis; + + if( ::rtl::math::approxEqual( fXOther, fXStart) ) + fYStart = fYEnd = fYOnYPlane; + else + fYStart = fYEnd = fYOther; + } + } + else + { + if( !m_pPosHelper->isSwapXAndY() ) + { + fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMaxX() : m_pPosHelper->getLogicMinX(); + fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMinY() : m_pPosHelper->getLogicMaxY(); + } + else + { + fXStart = fXEnd = m_pPosHelper->isMathematicalOrientationX() ? m_pPosHelper->getLogicMinX() : m_pPosHelper->getLogicMaxX(); + fYStart = fYEnd = m_pPosHelper->isMathematicalOrientationY() ? m_pPosHelper->getLogicMaxY() : m_pPosHelper->getLogicMinY(); + } + + if(3==m_nDimension) + { + rStart = getScreenPosition( fXStart, fYStart, fZStart ); + rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); + + double fDeltaX = rEnd.getX() - rStart.getX(); + + //only those points are candidates which are lying on exactly one wall as these are outer edges + tScreenPosAndLogicPosList aPosList; + aPosList.push_back( getScreenPosAndLogicPos( fXOther, fYOnYPlane, fMinZ ) ); + aPosList.push_back( getScreenPosAndLogicPos( fXOnXPlane, fYOther, fMinZ ) ); + + ::std::sort( aPosList.begin(), aPosList.end(), lcl_GreaterYPos() ); + ScreenPosAndLogicPos aBestPos( aPosList[0] ); + ScreenPosAndLogicPos aNotSoGoodPos( aPosList[1] ); + + //choose most bottom positions + if( !::rtl::math::approxEqual( fDeltaX, 0.0 ) ) // prefere left-right algnments + { + if( aBestPos.aScreenPos.getX() > aNotSoGoodPos.aScreenPos.getX() ) + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_RIGHT; + else + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_LEFT; + } + else + { + if( aBestPos.aScreenPos.getY() > aNotSoGoodPos.aScreenPos.getY() ) + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_BOTTOM; + else + m_aAxisProperties.m_aLabelAlignment = LABEL_ALIGN_TOP; + } + + m_aAxisProperties.m_fLabelDirectionSign = fDeltaX<0 ? -1 : 1; + if( !m_pPosHelper->isMathematicalOrientationZ() ) + m_aAxisProperties.m_fLabelDirectionSign *= -1; + + fXStart = fXEnd = aBestPos.fLogicX; + fYStart = fYEnd = aBestPos.fLogicY; + } + }//end 3D z axis + } + + rStart = getScreenPosition( fXStart, fYStart, fZStart ); + rEnd = getScreenPosition( fXEnd, fYEnd, fZEnd ); + + if(3==m_nDimension && !AxisHelper::isAxisPositioningEnabled() ) + m_aAxisProperties.m_fInnerDirectionSign = m_aAxisProperties.m_fLabelDirectionSign;//to behave like before + + if(3==m_nDimension && AxisHelper::isAxisPositioningEnabled() ) + { + double fDeltaX = rEnd.getX() - rStart.getX(); + double fDeltaY = rEnd.getY() - rStart.getY(); + + if( 2==m_nDimensionIndex ) + { + if( m_eLeftWallPos != CuboidPlanePosition_Left ) + { + m_aAxisProperties.m_fLabelDirectionSign *= -1.0; + m_aAxisProperties.m_fInnerDirectionSign *= -1.0; + } + + m_aAxisProperties.m_aLabelAlignment = + ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? + LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; + + if( ( fDeltaY<0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || + ( fDeltaY>0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) + m_aAxisProperties.m_aLabelAlignment = + ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_RIGHT ) ? + LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; + } + else if( fabs(fDeltaY) > fabs(fDeltaX) ) + { + if( m_eBackWallPos != CuboidPlanePosition_Back ) + { + m_aAxisProperties.m_fLabelDirectionSign *= -1.0; + m_aAxisProperties.m_fInnerDirectionSign *= -1.0; + } + + m_aAxisProperties.m_aLabelAlignment = + ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? + LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; + + if( ( fDeltaY<0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || + ( fDeltaY>0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) + m_aAxisProperties.m_aLabelAlignment = + ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_RIGHT ) ? + LABEL_ALIGN_LEFT : LABEL_ALIGN_RIGHT; + } + else + { + if( m_eBackWallPos != CuboidPlanePosition_Back ) + { + m_aAxisProperties.m_fLabelDirectionSign *= -1.0; + m_aAxisProperties.m_fInnerDirectionSign *= -1.0; + } + + m_aAxisProperties.m_aLabelAlignment = + ( m_aAxisProperties.m_fLabelDirectionSign<0 ) ? + LABEL_ALIGN_TOP : LABEL_ALIGN_BOTTOM; + + if( ( fDeltaX>0 && m_aScale.Orientation == AxisOrientation_REVERSE ) || + ( fDeltaX<0 && m_aScale.Orientation == AxisOrientation_MATHEMATICAL ) ) + m_aAxisProperties.m_aLabelAlignment = + ( m_aAxisProperties.m_aLabelAlignment==LABEL_ALIGN_TOP ) ? + LABEL_ALIGN_BOTTOM : LABEL_ALIGN_TOP; + } + } +} + +TickmarkHelper* VCartesianAxis::createTickmarkHelper() +{ + return createTickmarkHelper2D(); +} + +TickmarkHelper_2D* VCartesianAxis::createTickmarkHelper2D() +{ + B2DVector aStart, aEnd; + this->get2DAxisMainLine( aStart, aEnd, this->getLogicValueWhereMainLineCrossesOtherAxis() ); + + B2DVector aLabelLineStart, aLabelLineEnd; + this->get2DAxisMainLine( aLabelLineStart, aLabelLineEnd, this->getLogicValueWhereLabelLineCrossesOtherAxis() ); + + return new TickmarkHelper_2D( m_aScale, m_aIncrement, aStart, aEnd, aLabelLineStart-aStart ); +} + +void lcl_hideIdenticalScreenValues( TickIter& rTickIter ) +{ + TickInfo* pPreviousTickInfo = rTickIter.firstInfo(); + if(!pPreviousTickInfo) + return; + pPreviousTickInfo->bPaintIt = true; + for( TickInfo* pTickInfo = rTickIter.nextInfo(); pTickInfo; pTickInfo = rTickIter.nextInfo()) + { + pTickInfo->bPaintIt = + ( static_cast<sal_Int32>(pTickInfo->aTickScreenPosition.getX()) + != static_cast<sal_Int32>(pPreviousTickInfo->aTickScreenPosition.getX()) ) + || + ( static_cast<sal_Int32>(pTickInfo->aTickScreenPosition.getY()) + != static_cast<sal_Int32>(pPreviousTickInfo->aTickScreenPosition.getY()) ); + pPreviousTickInfo = pTickInfo; + } +} + +//'hide' tickmarks with identical screen values in aAllTickInfos +void VCartesianAxis::hideIdenticalScreenValues( ::std::vector< ::std::vector< TickInfo > >& rTickInfos ) const +{ + if( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) + { + sal_Int32 nCount = rTickInfos.size(); + for( sal_Int32 nN=0; nN<nCount; nN++ ) + { + PureTickIter aTickIter( rTickInfos[nN] ); + lcl_hideIdenticalScreenValues( aTickIter ); + } + } + else + { + EquidistantTickIter aTickIter( rTickInfos, m_aIncrement, 0, -1 ); + lcl_hideIdenticalScreenValues( aTickIter ); + } +} + +sal_Int32 VCartesianAxis::estimateMaximumAutoMainIncrementCount() +{ + sal_Int32 nRet = 10; + + if( m_nMaximumTextWidthSoFar==0 && m_nMaximumTextHeightSoFar==0 ) + return nRet; + + B2DVector aStart, aEnd; + this->get2DAxisMainLine( aStart, aEnd, this->getLogicValueWhereMainLineCrossesOtherAxis() ); + + sal_Int32 nMaxHeight = static_cast<sal_Int32>(fabs(aEnd.getY()-aStart.getY())); + sal_Int32 nMaxWidth = static_cast<sal_Int32>(fabs(aEnd.getX()-aStart.getX())); + + sal_Int32 nTotalAvailable = nMaxHeight; + sal_Int32 nSingleNeeded = m_nMaximumTextHeightSoFar; + + //for horizontal axis: + if( (m_nDimensionIndex == 0 && !m_aAxisProperties.m_bSwapXAndY) + || (m_nDimensionIndex == 1 && m_aAxisProperties.m_bSwapXAndY) ) + { + nTotalAvailable = nMaxWidth; + nSingleNeeded = m_nMaximumTextWidthSoFar; + } + + if( nSingleNeeded>0 ) + nRet = nTotalAvailable/nSingleNeeded; + + return nRet; +} + +void VCartesianAxis::doStaggeringOfLabels( const AxisLabelProperties& rAxisLabelProperties, TickmarkHelper_2D* pTickmarkHelper2D ) +{ + if( !pTickmarkHelper2D ) + return; + + if( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) + { + sal_Int32 nTextLevelCount = getTextLevelCount(); + B2DVector aCummulatedLabelsDistance(0,0); + for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) + { + ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); + if(apTickIter.get()) + { + if( nTextLevel>0 ) + lcl_shiftLables( *apTickIter.get(), aCummulatedLabelsDistance ); + aCummulatedLabelsDistance += lcl_getLabelsDistance( *apTickIter.get() + , pTickmarkHelper2D->getDistanceAxisTickToText( m_aAxisProperties ) ); + } + } + } + else if( rAxisLabelProperties.getIsStaggered() ) + { + EquidistantLabelIterator aInnerIter( m_aAllTickInfos, m_aIncrement + , rAxisLabelProperties.eStaggering, true, 0, 0 ); + EquidistantLabelIterator aOuterIter( m_aAllTickInfos, m_aIncrement + , rAxisLabelProperties.eStaggering, false, 0, 0 ); + + lcl_shiftLables( aOuterIter + , lcl_getLabelsDistance( aInnerIter + , pTickmarkHelper2D->getDistanceAxisTickToText( m_aAxisProperties ) ) ); + } +} + +void SAL_CALL VCartesianAxis::createLabels() +{ + if( !prepareShapeCreation() ) + return; + + //----------------------------------------- + //create labels + if( m_aAxisProperties.m_bDisplayLabels ) + { + std::auto_ptr< TickmarkHelper_2D > apTickmarkHelper2D( this->createTickmarkHelper2D() ); + TickmarkHelper_2D* pTickmarkHelper2D = apTickmarkHelper2D.get(); + if( !pTickmarkHelper2D ) + return; + + //----------------------------------------- + //get the transformed screen values for all tickmarks in aAllTickInfos + pTickmarkHelper2D->updateScreenValues( m_aAllTickInfos ); + //----------------------------------------- + //'hide' tickmarks with identical screen values in aAllTickInfos + hideIdenticalScreenValues( m_aAllTickInfos ); + + removeTextShapesFromTicks(); + + //create tick mark text shapes + sal_Int32 nTextLevelCount = getTextLevelCount(); + sal_Int32 nScreenDistanceBetweenTicks = -1; + for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) + { + ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); + if(apTickIter.get()) + { + if(nTextLevel==0) + { + nScreenDistanceBetweenTicks = TickmarkHelper_2D::getTickScreenDistance( *apTickIter.get() ); + if( nTextLevelCount>1 ) + nScreenDistanceBetweenTicks*=2; //the above used tick iter does contain also the sub ticks -> thus the given distance is only the half + } + + AxisLabelProperties aCopy(m_aAxisLabelProperties); + aCopy.bRhythmIsFix = true; + aCopy.nRhythm = 1; + AxisLabelProperties& rAxisLabelProperties = nTextLevel==0 ? m_aAxisLabelProperties : aCopy; + while( !createTextShapes( m_xTextTarget, *apTickIter.get(), rAxisLabelProperties, pTickmarkHelper2D, nScreenDistanceBetweenTicks ) ) + { + }; + } + } + doStaggeringOfLabels( m_aAxisLabelProperties, pTickmarkHelper2D ); + } +} + +void SAL_CALL VCartesianAxis::createMaximumLabels() +{ + TrueGuard aRecordMaximumTextSize(m_bRecordMaximumTextSize); + + if( !prepareShapeCreation() ) + return; + + //----------------------------------------- + //create labels + if( m_aAxisProperties.m_bDisplayLabels ) + { + std::auto_ptr< TickmarkHelper_2D > apTickmarkHelper2D( this->createTickmarkHelper2D() ); + TickmarkHelper_2D* pTickmarkHelper2D = apTickmarkHelper2D.get(); + if( !pTickmarkHelper2D ) + return; + + //----------------------------------------- + //get the transformed screen values for all tickmarks in aAllTickInfos + pTickmarkHelper2D->updateScreenValues( m_aAllTickInfos ); + + //create tick mark text shapes + //@todo: iterate through all tick depth wich should be labeled + + AxisLabelProperties aAxisLabelProperties( m_aAxisLabelProperties ); + if( isAutoStaggeringOfLabelsAllowed( aAxisLabelProperties, pTickmarkHelper2D->isHorizontalAxis(), pTickmarkHelper2D->isVerticalAxis() ) ) + aAxisLabelProperties.eStaggering = STAGGER_EVEN; + aAxisLabelProperties.bOverlapAllowed = true; + aAxisLabelProperties.bLineBreakAllowed = false; + sal_Int32 nTextLevelCount = getTextLevelCount(); + for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) + { + ::std::auto_ptr< TickIter > apTickIter = createMaximumLabelTickIterator( nTextLevel ); + if(apTickIter.get()) + { + while( !createTextShapes( m_xTextTarget, *apTickIter.get(), aAxisLabelProperties, pTickmarkHelper2D, -1 ) ) + { + }; + } + } + doStaggeringOfLabels( aAxisLabelProperties, pTickmarkHelper2D ); + } +} + +void SAL_CALL VCartesianAxis::updatePositions() +{ + //----------------------------------------- + //update positions of labels + if( m_aAxisProperties.m_bDisplayLabels ) + { + std::auto_ptr< TickmarkHelper_2D > apTickmarkHelper2D( this->createTickmarkHelper2D() ); + TickmarkHelper_2D* pTickmarkHelper2D = apTickmarkHelper2D.get(); + if( !pTickmarkHelper2D ) + return; + + //----------------------------------------- + //update positions of all existing text shapes + pTickmarkHelper2D->updateScreenValues( m_aAllTickInfos ); + + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = m_aAllTickInfos.begin(); + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = m_aAllTickInfos.end(); + for( ; aDepthIter != aDepthEnd; aDepthIter++ ) + { + ::std::vector< TickInfo >::iterator aTickIter = aDepthIter->begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = aDepthIter->end(); + for( ; aTickIter != aTickEnd; aTickIter++ ) + { + TickInfo& rTickInfo = (*aTickIter); + Reference< drawing::XShape > xShape2DText( rTickInfo.xTextShape ); + if( xShape2DText.is() ) + { + B2DVector aTextToTickDistance( pTickmarkHelper2D->getDistanceAxisTickToText( m_aAxisProperties, true ) ); + B2DVector aTickScreenPos2D( rTickInfo.aTickScreenPosition ); + aTickScreenPos2D += aTextToTickDistance; + awt::Point aAnchorScreenPosition2D( + static_cast<sal_Int32>(aTickScreenPos2D.getX()) + ,static_cast<sal_Int32>(aTickScreenPos2D.getY())); + + // #i78696# use mathematically correct rotation now + const double fRotationAnglePi(m_aAxisLabelProperties.fRotationAngleDegree * (F_PI / -180.0)); + uno::Any aATransformation = ShapeFactory::makeTransformation(aAnchorScreenPosition2D, fRotationAnglePi); + + //set new position + uno::Reference< beans::XPropertySet > xProp( xShape2DText, uno::UNO_QUERY ); + if( xProp.is() ) + { + try + { + xProp->setPropertyValue( C2U( "Transformation" ), aATransformation ); + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + } + + //correctPositionForRotation + LabelPositionHelper::correctPositionForRotation( xShape2DText + , m_aAxisProperties.m_aLabelAlignment, m_aAxisLabelProperties.fRotationAngleDegree, false ); + } + } + } + + doStaggeringOfLabels( m_aAxisLabelProperties, pTickmarkHelper2D ); + } +} + +void VCartesianAxis::createTickMarkLineShapes( ::std::vector< TickInfo >& rTickInfos, const TickmarkProperties& rTickmarkProperties, TickmarkHelper_2D& rTickmarkHelper2D, bool bOnlyAtLabels ) +{ + sal_Int32 nPointCount = rTickInfos.size(); + drawing::PointSequenceSequence aPoints(2*nPointCount); + + ::std::vector< TickInfo >::const_iterator aTickIter = rTickInfos.begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = rTickInfos.end(); + sal_Int32 nN = 0; + for( ; aTickIter != aTickEnd; aTickIter++ ) + { + if( !(*aTickIter).bPaintIt ) + continue; + + bool bTicksAtLabels = ( m_aAxisProperties.m_eTickmarkPos != ::com::sun::star::chart::ChartAxisMarkPosition_AT_AXIS ); + double fInnerDirectionSign = m_aAxisProperties.m_fInnerDirectionSign; + if( bTicksAtLabels && m_aAxisProperties.m_eLabelPos == ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END ) + fInnerDirectionSign *= -1.0; + bTicksAtLabels = bTicksAtLabels || bOnlyAtLabels; + //add ticks at labels: + rTickmarkHelper2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue + , fInnerDirectionSign , rTickmarkProperties, bTicksAtLabels ); + //add ticks at axis (without lables): + if( !bOnlyAtLabels && m_aAxisProperties.m_eTickmarkPos == ::com::sun::star::chart::ChartAxisMarkPosition_AT_LABELS_AND_AXIS ) + rTickmarkHelper2D.addPointSequenceForTickLine( aPoints, nN++, (*aTickIter).fScaledTickValue + , m_aAxisProperties.m_fInnerDirectionSign, rTickmarkProperties, !bTicksAtLabels ); + } + aPoints.realloc(nN); + m_pShapeFactory->createLine2D( m_xGroupShape_Shapes, aPoints + , &rTickmarkProperties.aLineProperties ); +} + +void SAL_CALL VCartesianAxis::createShapes() +{ + if( !prepareShapeCreation() ) + return; + + std::auto_ptr< TickmarkHelper_2D > apTickmarkHelper2D( this->createTickmarkHelper2D() ); + TickmarkHelper_2D* pTickmarkHelper2D = apTickmarkHelper2D.get(); + if( !pTickmarkHelper2D ) + return; + + //----------------------------------------- + //create line shapes + if(2==m_nDimension) + { + //----------------------------------------- + //create extra long ticks to separate complex categories (create them only there where the labels are) + if( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) + { + ::std::vector< ::std::vector< TickInfo > > aComplexTickInfos; + createAllTickInfosFromComplexCategories( aComplexTickInfos, true ); + pTickmarkHelper2D->updateScreenValues( aComplexTickInfos ); + hideIdenticalScreenValues( aComplexTickInfos ); + + ::std::vector<TickmarkProperties> aTickmarkPropertiesList; + static bool bIncludeSpaceBetweenTickAndText = false; + sal_Int32 nOffset = static_cast<sal_Int32>(pTickmarkHelper2D->getDistanceAxisTickToText( m_aAxisProperties, false, bIncludeSpaceBetweenTickAndText ).getLength()); + sal_Int32 nTextLevelCount = getTextLevelCount(); + for( sal_Int32 nTextLevel=0; nTextLevel<nTextLevelCount; nTextLevel++ ) + { + ::std::auto_ptr< TickIter > apTickIter = createLabelTickIterator( nTextLevel ); + if( apTickIter.get() ) + { + B2DVector aLabelsDistance( lcl_getLabelsDistance( *apTickIter.get(), pTickmarkHelper2D->getDistanceAxisTickToText( m_aAxisProperties, false ) ) ); + sal_Int32 nCurrentLength = static_cast<sal_Int32>(aLabelsDistance.getLength()); + aTickmarkPropertiesList.push_back( m_aAxisProperties.makeTickmarkPropertiesForComplexCategories( nOffset + nCurrentLength, 0, nTextLevel ) ); + nOffset += nCurrentLength; + } + } + + sal_Int32 nTickmarkPropertiesCount = aTickmarkPropertiesList.size(); + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = aComplexTickInfos.begin(); + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = aComplexTickInfos.end(); + for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; aDepthIter++, nDepth++ ) + { + if(nDepth==0 && !m_aAxisProperties.m_nMajorTickmarks) + continue; + createTickMarkLineShapes( *aDepthIter, aTickmarkPropertiesList[nDepth], *pTickmarkHelper2D, true /*bOnlyAtLabels*/ ); + } + } + //----------------------------------------- + //create normal ticks for major and minor intervals + { + ::std::vector< ::std::vector< TickInfo > > aAllShiftedTickInfos; + if( m_aIncrement.ShiftedPosition || ( m_aAxisProperties.m_bComplexCategories && m_bUseTextLabels ) ) + { + pTickmarkHelper2D->getAllTicksShifted( aAllShiftedTickInfos ); + pTickmarkHelper2D->updateScreenValues( aAllShiftedTickInfos ); + hideIdenticalScreenValues( aAllShiftedTickInfos ); + } + ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos = m_aIncrement.ShiftedPosition ? aAllShiftedTickInfos : m_aAllTickInfos; + + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = rAllTickInfos.begin(); + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = rAllTickInfos.end(); + if(aDepthIter == aDepthEnd)//no tickmarks at all + return; + + sal_Int32 nTickmarkPropertiesCount = m_aAxisProperties.m_aTickmarkPropertiesList.size(); + for( sal_Int32 nDepth=0; aDepthIter != aDepthEnd && nDepth < nTickmarkPropertiesCount; aDepthIter++, nDepth++ ) + createTickMarkLineShapes( *aDepthIter, m_aAxisProperties.m_aTickmarkPropertiesList[nDepth], *pTickmarkHelper2D, false /*bOnlyAtLabels*/ ); + } + //----------------------------------------- + //create axis main lines + //it serves also as the handle shape for the axis selection + { + drawing::PointSequenceSequence aPoints(1); + apTickmarkHelper2D->createPointSequenceForAxisMainLine( aPoints ); + Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( + m_xGroupShape_Shapes, aPoints + , &m_aAxisProperties.m_aLineProperties ); + //because of this name this line will be used for marking the axis + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + } + //----------------------------------------- + //create an additional line at NULL + if( !AxisHelper::isAxisPositioningEnabled() ) + { + double fExtraLineCrossesOtherAxis; + if( getLogicValueWhereExtraLineCrossesOtherAxis(fExtraLineCrossesOtherAxis) ) + { + B2DVector aStart, aEnd; + this->get2DAxisMainLine( aStart, aEnd, fExtraLineCrossesOtherAxis ); + drawing::PointSequenceSequence aPoints( lcl_makePointSequence(aStart,aEnd) ); + Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( + m_xGroupShape_Shapes, aPoints, &m_aAxisProperties.m_aLineProperties ); + } + } + } + + //createLabels(); +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VCartesianAxis.hxx b/chart2/source/view/axes/VCartesianAxis.hxx new file mode 100644 index 000000000000..4fbcd2409196 --- /dev/null +++ b/chart2/source/view/axes/VCartesianAxis.hxx @@ -0,0 +1,146 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VCARTESIANAXIS_HXX +#define _CHART2_VCARTESIANAXIS_HXX + +#include "VAxisBase.hxx" +#include <basegfx/vector/b2dvector.hxx> + +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class VCartesianAxis : public VAxisBase +{ + //------------------------------------------------------------------------- + // public methods + //------------------------------------------------------------------------- +public: + VCartesianAxis( const AxisProperties& rAxisProperties + , const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , PlottingPositionHelper* pPosHelper = NULL //takes ownership + ); + + virtual ~VCartesianAxis(); + + //------------------------------------------------------------------------- + // partly chart2::XPlotter + //------------------------------------------------------------------------- + + /* + virtual ::rtl::OUString SAL_CALL getCoordinateSystemTypeID( ) throw (::com::sun::star::uno::RuntimeException); + virtual void SAL_CALL setTransformation( const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToLogicTarget, const ::com::sun::star::uno::Reference< ::com::sun::star::chart2::XTransformation >& xTransformationToFinalPage ) throw (::com::sun::star::uno::RuntimeException); + */ + + virtual void SAL_CALL createMaximumLabels(); + virtual void SAL_CALL createLabels(); + virtual void SAL_CALL updatePositions(); + + virtual void SAL_CALL createShapes(); + + virtual sal_Int32 estimateMaximumAutoMainIncrementCount(); + virtual void createAllTickInfos( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ); + void createAllTickInfosFromComplexCategories( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos, bool bShiftedPosition ); + + ::std::auto_ptr< TickIter > createLabelTickIterator( sal_Int32 nTextLevel ); + ::std::auto_ptr< TickIter > createMaximumLabelTickIterator( sal_Int32 nTextLevel ); + sal_Int32 getTextLevelCount() const; + + //------------------------------------------------------------------------- + virtual TickmarkHelper* createTickmarkHelper(); + + //------------------------------------------------------------------------- + double getLogicValueWhereMainLineCrossesOtherAxis() const; + double getLogicValueWhereLabelLineCrossesOtherAxis() const; + bool getLogicValueWhereExtraLineCrossesOtherAxis( double& fCrossesOtherAxis) const; + void get2DAxisMainLine( ::basegfx::B2DVector& rStart, ::basegfx::B2DVector& rEnd, double fCrossesOtherAxis ); + + //------------------------------------------------------------------------- + //Layout interface for cartesian axes: + + //the returned value describes the minimum size that is necessary + //for the text labels in the direction orthogonal to the axis + //(for an y-axis a width is returned; in case of an x-axis the value describes a height) + //the return value is measured in screen dimension + //As an example the MinimumOrthogonalSize of an x-axis equals the + //Font Height if the label properties allow for labels parallel to the axis. +// sal_Int32 calculateMinimumOrthogonalSize( /*... parallel...*/ ); + //Minimum->Preferred + + //returns true if the MinimumOrthogonalSize can be calculated + //with the creation of at most one text shape + //(this is e.g. true if the parameters allow for labels parallel to the axis.) +// sal_bool canQuicklyCalculateMinimumOrthogonalSize(); + + + //------------------------------------------------------------------------- + //------------------------------------------------------------------------- + + struct ScreenPosAndLogicPos + { + double fLogicX; + double fLogicY; + double fLogicZ; + + B2DVector aScreenPos; + }; + +protected: //methods + bool createTextShapes( const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes >& xTarget + , TickIter& rTickIter + , AxisLabelProperties& rAxisLabelProperties + , TickmarkHelper_2D* pTickmarkHelper + , sal_Int32 nScreenDistanceBetweenTicks ); + + void createTickMarkLineShapes( ::std::vector< TickInfo >& rTickInfos, const TickmarkProperties& rTickmarkProperties, TickmarkHelper_2D& rTickmarkHelper2D, bool bOnlyAtLabels ); + + TickmarkHelper_2D* createTickmarkHelper2D(); + void hideIdenticalScreenValues( ::std::vector< ::std::vector< TickInfo > >& rTickInfos ) const; + + void doStaggeringOfLabels( const AxisLabelProperties& rAxisLabelProperties + , TickmarkHelper_2D* pTickmarkHelper2D ); + bool isAutoStaggeringOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties + , bool bIsHorizontalAxis, bool bIsVerticalAxis ); + bool isBreakOfLabelsAllowed( const AxisLabelProperties& rAxisLabelProperties, bool bIsHorizontalAxis ); + + ::basegfx::B2DVector getScreenPosition( double fLogicX, double fLogicY, double fLogicZ ) const; + ScreenPosAndLogicPos getScreenPosAndLogicPos( double fLogicX, double fLogicY, double fLogicZ ) const; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VCartesianCoordinateSystem.cxx b/chart2/source/view/axes/VCartesianCoordinateSystem.cxx new file mode 100644 index 000000000000..a196397e0e9f --- /dev/null +++ b/chart2/source/view/axes/VCartesianCoordinateSystem.cxx @@ -0,0 +1,250 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "VCartesianCoordinateSystem.hxx" +#include "VCartesianGrid.hxx" +#include "VCartesianAxis.hxx" +#include "macros.hxx" +#include "AxisIndexDefines.hxx" +#include "AxisHelper.hxx" +#include "ChartTypeHelper.hxx" + +//for auto_ptr +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; + +//............................................................................. + +class TextualDataProvider : public ::cppu::WeakImplHelper1< + ::com::sun::star::chart2::data::XTextualDataSequence + > +{ +public: + TextualDataProvider( const uno::Sequence< ::rtl::OUString >& rTextSequence ) + : m_aTextSequence( rTextSequence ) + { + } + virtual ~TextualDataProvider() + { + } + + //XTextualDataSequence + virtual uno::Sequence< ::rtl::OUString > SAL_CALL getTextualData() + throw ( uno::RuntimeException) + { + return m_aTextSequence; + } + +private: //member + uno::Sequence< ::rtl::OUString > m_aTextSequence; +}; + +//............................................................................. + +VCartesianCoordinateSystem::VCartesianCoordinateSystem( const Reference< XCoordinateSystem >& xCooSys ) + : VCoordinateSystem(xCooSys) +{ +} + +VCartesianCoordinateSystem::~VCartesianCoordinateSystem() +{ +} + +void VCartesianCoordinateSystem::createGridShapes() +{ + if(!m_xLogicTargetForGrids.is() || !m_xFinalTarget.is() ) + return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + for( sal_Int32 nDimensionIndex=0; nDimensionIndex<3; nDimensionIndex++) + { + sal_Int32 nAxisIndex = MAIN_AXIS_INDEX; + Reference< XAxis > xAxis( AxisHelper::getAxis( nDimensionIndex, nAxisIndex, m_xCooSysModel ) ); + if(!xAxis.is() || !AxisHelper::shouldAxisBeDisplayed( xAxis, m_xCooSysModel )) + continue; + + VCartesianGrid aGrid(nDimensionIndex,nDimensionCount,this->getGridListFromAxis( xAxis )); + aGrid.setExplicitScaleAndIncrement( this->getExplicitScale(nDimensionIndex,nAxisIndex) + , this->getExplicitIncrement(nDimensionIndex,nAxisIndex) ); + aGrid.set3DWallPositions( m_eLeftWallPos, m_eBackWallPos, m_eBottomPos ); + + aGrid.initPlotter(m_xLogicTargetForGrids,m_xFinalTarget,m_xShapeFactory + , this->createCIDForGrid( xAxis,nDimensionIndex,nAxisIndex ) ); + if(2==nDimensionCount) + aGrid.setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + aGrid.setScales( this->getExplicitScales(nDimensionIndex,nAxisIndex), bSwapXAndY ); + aGrid.createShapes(); + } +} + +void VCartesianCoordinateSystem::createVAxisList( + const uno::Reference< util::XNumberFormatsSupplier > & xNumberFormatsSupplier + , const awt::Size& rFontReferenceSize + , const awt::Rectangle& rMaximumSpaceForLabels + ) +{ + m_aAxisMap.clear(); + + //if(!m_xLogicTargetForAxes.is() || !m_xFinalTarget.is() || !m_xCooSysModel.is() ) + // return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + if(nDimensionCount<=0) + return; + + sal_Int32 nDimensionIndex = 0; + + for( nDimensionIndex = 0; nDimensionIndex < nDimensionCount; nDimensionIndex++ ) + { + sal_Int32 nMaxAxisIndex = m_xCooSysModel->getMaximumAxisIndexByDimension(nDimensionIndex); + for( sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaxAxisIndex; nAxisIndex++ ) + { + Reference< XAxis > xAxis = this->getAxisByDimension(nDimensionIndex,nAxisIndex); + if(!xAxis.is() || !AxisHelper::shouldAxisBeDisplayed( xAxis, m_xCooSysModel )) + continue; + + AxisProperties aAxisProperties(xAxis,this->getExplicitCategoriesProvider()); + aAxisProperties.m_nDimensionIndex = nDimensionIndex; + aAxisProperties.m_bSwapXAndY = bSwapXAndY; + aAxisProperties.m_bIsMainAxis = (nAxisIndex==0); + Reference< XAxis > xCrossingMainAxis( AxisHelper::getCrossingMainAxis( xAxis, m_xCooSysModel ) ); + if( xCrossingMainAxis.is() ) + { + ScaleData aCrossingScale( xCrossingMainAxis->getScaleData() ); + aAxisProperties.m_bCrossingAxisHasReverseDirection = (AxisOrientation_REVERSE==aCrossingScale.Orientation); + + if( aCrossingScale.AxisType == AxisType::CATEGORY ) + { + aAxisProperties.m_bCrossingAxisIsCategoryAxes = true; + aAxisProperties.m_bAxisBetweenCategories = ChartTypeHelper::shiftTicksAtXAxisPerDefault( AxisHelper::getChartTypeByIndex( m_xCooSysModel, 0 ) ) + || ( aAxisProperties.m_pExplicitCategoriesProvider && aAxisProperties.m_pExplicitCategoriesProvider->hasComplexCategories() ); + } + } + + if( nDimensionIndex == 2 ) + { + aAxisProperties.m_xAxisTextProvider = new TextualDataProvider( m_aSeriesNamesForZAxis ); + + //for the z axis copy the positioning properties from the x axis (or from the y axis for swapped coordinate systems) + Reference< XAxis > xSisterAxis( AxisHelper::getCrossingMainAxis( xCrossingMainAxis, m_xCooSysModel ) ); + aAxisProperties.initAxisPositioning( Reference< beans::XPropertySet >( xSisterAxis, uno::UNO_QUERY) ); + } + aAxisProperties.init(true); + if(aAxisProperties.m_bDisplayLabels) + aAxisProperties.m_nNumberFormatKey = this->getNumberFormatKeyForAxis( xAxis, xNumberFormatsSupplier ); + + if( nDimensionIndex == 0 && aAxisProperties.m_nAxisType == AxisType::CATEGORY + && aAxisProperties.m_pExplicitCategoriesProvider ) + { + if( aAxisProperties.m_pExplicitCategoriesProvider->hasComplexCategories() ) + aAxisProperties.m_bComplexCategories = true; + } + //------------------- + ::boost::shared_ptr< VAxisBase > apVAxis( new VCartesianAxis(aAxisProperties,xNumberFormatsSupplier,nDimensionIndex,nDimensionCount) ); + tFullAxisIndex aFullAxisIndex( nDimensionIndex, nAxisIndex ); + m_aAxisMap[aFullAxisIndex] = apVAxis; + apVAxis->set3DWallPositions( m_eLeftWallPos, m_eBackWallPos, m_eBottomPos ); + + //apVAxis->setExplicitScaleAndIncrement( this->getExplicitScale( nDimensionIndex, nAxisIndex ), this->getExplicitIncrement( nDimensionIndex, nAxisIndex ) ); + //apVAxis->initPlotter(m_xLogicTargetForAxes,m_xFinalTarget,m_xShapeFactory + // , this->createCIDForAxis( xAxis, nDimensionIndex, nAxisIndex ) ); + //if(2==nDimensionCount) + // apVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + //apVAxis->setScales( this->getExplicitScales(nDimensionIndex,nAxisIndex), bSwapXAndY ); + apVAxis->initAxisLabelProperties(rFontReferenceSize,rMaximumSpaceForLabels); + } + } +} + +void VCartesianCoordinateSystem::initVAxisInList() +{ + if(!m_xLogicTargetForAxes.is() || !m_xFinalTarget.is() || !m_xCooSysModel.is() ) + return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + sal_Int32 nDimensionIndex = aIt->first.first; + sal_Int32 nAxisIndex = aIt->first.second; + pVAxis->setExplicitScaleAndIncrement( this->getExplicitScale( nDimensionIndex, nAxisIndex ), this->getExplicitIncrement( nDimensionIndex, nAxisIndex ) ); + pVAxis->initPlotter(m_xLogicTargetForAxes,m_xFinalTarget,m_xShapeFactory + , this->createCIDForAxis( getAxisByDimension( nDimensionIndex, nAxisIndex ), nDimensionIndex, nAxisIndex ) ); + if(2==nDimensionCount) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + pVAxis->setScales( this->getExplicitScales(nDimensionIndex,nAxisIndex), bSwapXAndY ); + } + } +} + +void VCartesianCoordinateSystem::updateScalesAndIncrementsOnAxes() +{ + if(!m_xLogicTargetForAxes.is() || !m_xFinalTarget.is() || !m_xCooSysModel.is() ) + return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + sal_Int32 nDimensionIndex = aIt->first.first; + sal_Int32 nAxisIndex = aIt->first.second; + pVAxis->setExplicitScaleAndIncrement( this->getExplicitScale( nDimensionIndex, nAxisIndex ), this->getExplicitIncrement( nDimensionIndex, nAxisIndex ) ); + if(2==nDimensionCount) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + pVAxis->setScales( this->getExplicitScales(nDimensionIndex,nAxisIndex), bSwapXAndY ); + } + } +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VCartesianCoordinateSystem.hxx b/chart2/source/view/axes/VCartesianCoordinateSystem.hxx new file mode 100644 index 000000000000..b28400c6f906 --- /dev/null +++ b/chart2/source/view/axes/VCartesianCoordinateSystem.hxx @@ -0,0 +1,64 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VCARTESIAN_COORDINATESYSTEM_HXX +#define _CHART2_VCARTESIAN_COORDINATESYSTEM_HXX + +#include "VCoordinateSystem.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ +class VCartesianCoordinateSystem : public VCoordinateSystem +{ +public: + VCartesianCoordinateSystem( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XCoordinateSystem >& xCooSys ); + virtual ~VCartesianCoordinateSystem(); + + virtual void createVAxisList( + const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier > & xNumberFormatsSupplier + , const ::com::sun::star::awt::Size& rFontReferenceSize + , const ::com::sun::star::awt::Rectangle& rMaximumSpaceForLabels ); + + virtual void initVAxisInList(); + virtual void updateScalesAndIncrementsOnAxes(); + + virtual void createGridShapes(); + +private: + VCartesianCoordinateSystem(); +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VCartesianGrid.cxx b/chart2/source/view/axes/VCartesianGrid.cxx new file mode 100644 index 000000000000..7fe6b9937ca6 --- /dev/null +++ b/chart2/source/view/axes/VCartesianGrid.cxx @@ -0,0 +1,339 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "VCartesianGrid.hxx" +#include "TickmarkHelper.hxx" +#include "PlottingPositionHelper.hxx" +#include "ShapeFactory.hxx" +#include "ObjectIdentifier.hxx" +#include "macros.hxx" +#include "CommonConverters.hxx" +#include "AxisHelper.hxx" +#include <com/sun/star/drawing/PointSequenceSequence.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> + +#include <vector> +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +struct GridLinePoints +{ + Sequence< double > P0; + Sequence< double > P1; + Sequence< double > P2; + + GridLinePoints( const PlottingPositionHelper* pPosHelper, sal_Int32 nDimensionIndex + , CuboidPlanePosition eLeftWallPos=CuboidPlanePosition_Left + , CuboidPlanePosition eBackWallPos=CuboidPlanePosition_Back + , CuboidPlanePosition eBottomPos=CuboidPlanePosition_Bottom ); + void update( double fScaledTickValue ); + + sal_Int32 m_nDimensionIndex; +}; + +GridLinePoints::GridLinePoints( const PlottingPositionHelper* pPosHelper, sal_Int32 nDimensionIndex + , CuboidPlanePosition eLeftWallPos + , CuboidPlanePosition eBackWallPos + , CuboidPlanePosition eBottomPos ) + : m_nDimensionIndex(nDimensionIndex) +{ + double MinX = pPosHelper->getLogicMinX(); + double MinY = pPosHelper->getLogicMinY(); + double MinZ = pPosHelper->getLogicMinZ(); + double MaxX = pPosHelper->getLogicMaxX(); + double MaxY = pPosHelper->getLogicMaxY(); + double MaxZ = pPosHelper->getLogicMaxZ(); + + pPosHelper->doLogicScaling( &MinX,&MinY,&MinZ ); + pPosHelper->doLogicScaling( &MaxX,&MaxY,&MaxZ ); + + if(!pPosHelper->isMathematicalOrientationX()) + { + double fHelp = MinX; + MinX = MaxX; + MaxX = fHelp; + } + if(!pPosHelper->isMathematicalOrientationY()) + { + double fHelp = MinY; + MinY = MaxY; + MaxY = fHelp; + } + if(pPosHelper->isMathematicalOrientationZ())//z axis in draw is reverse to mathematical + { + double fHelp = MinZ; + MinZ = MaxZ; + MaxZ = fHelp; + } + bool bSwapXY = pPosHelper->isSwapXAndY(); + + P0.realloc(3); + P1.realloc(3); + P2.realloc(3); + + //P0: point on 'back' wall, not on 'left' wall + //P1: point on both walls + //P2: point on 'left' wall not on 'back' wall + + P0[0]=P1[0]=P2[0]= (CuboidPlanePosition_Left == eLeftWallPos || bSwapXY) ? MinX : MaxX; + P0[1]=P1[1]=P2[1]= (CuboidPlanePosition_Left == eLeftWallPos || !bSwapXY) ? MinY : MaxY; + P0[2]=P1[2]=P2[2]= (CuboidPlanePosition_Back == eBackWallPos) ? MinZ : MaxZ; + + if(m_nDimensionIndex==0) + { + P0[1]= (CuboidPlanePosition_Left == eLeftWallPos || !bSwapXY) ? MaxY : MinY; + P2[2]= (CuboidPlanePosition_Back == eBackWallPos) ? MaxZ : MinZ; + if( CuboidPlanePosition_Bottom != eBottomPos && !bSwapXY ) + P2=P1; + } + else if(m_nDimensionIndex==1) + { + P0[0]= (CuboidPlanePosition_Left == eLeftWallPos || bSwapXY) ? MaxX : MinX; + P2[2]= (CuboidPlanePosition_Back == eBackWallPos) ? MaxZ : MinZ; + if( CuboidPlanePosition_Bottom != eBottomPos && bSwapXY ) + P2=P1; + } + else if(m_nDimensionIndex==2) + { + P0[0]= (CuboidPlanePosition_Left == eLeftWallPos || bSwapXY) ? MaxX : MinX; + P2[1]= (CuboidPlanePosition_Left == eLeftWallPos || !bSwapXY) ? MaxY : MinY; + if( CuboidPlanePosition_Bottom != eBottomPos ) + { + if( !bSwapXY ) + P0=P1; + else + P2=P1; + } + } +} + +void GridLinePoints::update( double fScaledTickValue ) +{ + P0[m_nDimensionIndex] = P1[m_nDimensionIndex] = P2[m_nDimensionIndex] = fScaledTickValue; +} + +void addLine2D( drawing::PointSequenceSequence& rPoints, sal_Int32 nIndex + , const GridLinePoints& rScaledLogicPoints + , const Reference< XTransformation > & xTransformation + ) +{ + drawing::Position3D aPA = SequenceToPosition3D( xTransformation->transform( rScaledLogicPoints.P0 ) ); + drawing::Position3D aPB = SequenceToPosition3D( xTransformation->transform( rScaledLogicPoints.P1 ) ); + + rPoints[nIndex].realloc(2); + rPoints[nIndex][0].X = static_cast<sal_Int32>(aPA.PositionX); + rPoints[nIndex][0].Y = static_cast<sal_Int32>(aPA.PositionY); + rPoints[nIndex][1].X = static_cast<sal_Int32>(aPB.PositionX); + rPoints[nIndex][1].Y = static_cast<sal_Int32>(aPB.PositionY); +} + +void addLine3D( drawing::PolyPolygonShape3D& rPoints, sal_Int32 nIndex + , const GridLinePoints& rBasePoints + , const Reference< XTransformation > & xTransformation ) +{ + drawing::Position3D aPoint = SequenceToPosition3D( xTransformation->transform( rBasePoints.P0 ) ); + AddPointToPoly( rPoints, aPoint, nIndex ); + aPoint = SequenceToPosition3D( xTransformation->transform( rBasePoints.P1 ) ); + AddPointToPoly( rPoints, aPoint, nIndex ); + aPoint = SequenceToPosition3D( xTransformation->transform( rBasePoints.P2 ) ); + AddPointToPoly( rPoints, aPoint, nIndex ); +} + +//--------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------- +//--------------------------------------------------------------------------------- + +VCartesianGrid::VCartesianGrid( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , const Sequence< Reference< beans::XPropertySet > > & rGridPropertiesList ) + : VAxisOrGridBase( nDimensionIndex, nDimensionCount ) + , m_aGridPropertiesList( rGridPropertiesList ) +{ + m_pPosHelper = new PlottingPositionHelper(); +} + +VCartesianGrid::~VCartesianGrid() +{ + delete m_pPosHelper; + m_pPosHelper = NULL; +} + +//static +void VCartesianGrid::fillLinePropertiesFromGridModel( ::std::vector<VLineProperties>& rLinePropertiesList + , const Sequence< Reference< beans::XPropertySet > > & rGridPropertiesList ) +{ + rLinePropertiesList.clear(); + if( !rGridPropertiesList.getLength() ) + return; + + VLineProperties aLineProperties; + for( sal_Int32 nN=0; nN < rGridPropertiesList.getLength(); nN++ ) + { + if(!AxisHelper::isGridVisible( rGridPropertiesList[nN] )) + aLineProperties.LineStyle = uno::makeAny( drawing::LineStyle_NONE ); + else + aLineProperties.initFromPropertySet( rGridPropertiesList[nN] ); + rLinePropertiesList.push_back(aLineProperties); + } +}; + +void SAL_CALL VCartesianGrid::createShapes() +{ + if(!m_aGridPropertiesList.getLength()) + return; + //somehow equal to axis tickmarks + + //----------------------------------------- + //create named group shape + Reference< drawing::XShapes > xGroupShape_Shapes( + this->createGroupShape( m_xLogicTarget, m_aCID ) ); + + if(!xGroupShape_Shapes.is()) + return; + //----------------------------------------- + + ::std::vector<VLineProperties> aLinePropertiesList; + fillLinePropertiesFromGridModel( aLinePropertiesList, m_aGridPropertiesList ); + + //----------------------------------------- + //create all scaled tickmark values + std::auto_ptr< TickmarkHelper > apTickmarkHelper( this->createTickmarkHelper() ); + TickmarkHelper& aTickmarkHelper = *apTickmarkHelper.get(); + ::std::vector< ::std::vector< TickInfo > > aAllTickInfos; + if( m_aIncrement.ShiftedPosition ) + aTickmarkHelper.getAllTicksShifted( aAllTickInfos ); + else + aTickmarkHelper.getAllTicks( aAllTickInfos ); + + //----------------------------------------- + //create tick mark line shapes + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = aAllTickInfos.begin(); + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = aAllTickInfos.end(); + + if(aDepthIter == aDepthEnd)//no tickmarks at all + return; + + + sal_Int32 nLinePropertiesCount = aLinePropertiesList.size(); + for( sal_Int32 nDepth=0 + ; aDepthIter != aDepthEnd && nDepth < nLinePropertiesCount + ; aDepthIter++, nDepth++ ) + { + if( !aLinePropertiesList[nDepth].isLineVisible() ) + continue; + + Reference< drawing::XShapes > xTarget( xGroupShape_Shapes ); + if( nDepth > 0 ) + { + xTarget.set( this->createGroupShape( m_xLogicTarget + , ObjectIdentifier::addChildParticle( m_aCID, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_SUBGRID, nDepth-1 ) ) + ) ); + if(!xTarget.is()) + xTarget.set( xGroupShape_Shapes ); + } + + if(2==m_nDimension) + { + + GridLinePoints aGridLinePoints( m_pPosHelper, m_nDimensionIndex ); + + sal_Int32 nPointCount = (*aDepthIter).size(); + drawing::PointSequenceSequence aPoints(nPointCount); + + ::std::vector< TickInfo >::const_iterator aTickIter = (*aDepthIter).begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = (*aDepthIter).end(); + sal_Int32 nRealPointCount = 0; + for( ; aTickIter != aTickEnd; aTickIter++ ) + { + if( !(*aTickIter).bPaintIt ) + continue; + aGridLinePoints.update( (*aTickIter).fScaledTickValue ); + addLine2D( aPoints, nRealPointCount, aGridLinePoints, m_pPosHelper->getTransformationScaledLogicToScene() ); + nRealPointCount++; + } + aPoints.realloc(nRealPointCount); + m_pShapeFactory->createLine2D( xTarget, aPoints, &aLinePropertiesList[nDepth] ); + + //prepare polygon for handle shape: + drawing::PointSequenceSequence aHandlesPoints(1); + sal_Int32 nOldHandleCount = aHandlesPoints[0].getLength(); + aHandlesPoints[0].realloc(nOldHandleCount+nRealPointCount); + for( sal_Int32 nN = 0; nN<nRealPointCount; nN++) + aHandlesPoints[0][nOldHandleCount+nN] = aPoints[nN][1]; + + //create handle shape: + VLineProperties aHandleLineProperties; + aHandleLineProperties.LineStyle = uno::makeAny( drawing::LineStyle_NONE ); + Reference< drawing::XShape > xHandleShape = + m_pShapeFactory->createLine2D( xTarget, aHandlesPoints, &aHandleLineProperties ); + m_pShapeFactory->setShapeName( xHandleShape, C2U("HandlesOnly") ); + } + //----------------------------------------- + else //if(2!=m_nDimension) + { + GridLinePoints aGridLinePoints( m_pPosHelper, m_nDimensionIndex, m_eLeftWallPos, m_eBackWallPos, m_eBottomPos ); + + sal_Int32 nPointCount = (*aDepthIter).size(); + drawing::PolyPolygonShape3D aPoints; + aPoints.SequenceX.realloc(nPointCount); + aPoints.SequenceY.realloc(nPointCount); + aPoints.SequenceZ.realloc(nPointCount); + + ::std::vector< TickInfo >::const_iterator aTickIter = (*aDepthIter).begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = (*aDepthIter).end(); + sal_Int32 nRealPointCount = 0; + sal_Int32 nPolyIndex = 0; + for( ; aTickIter != aTickEnd; aTickIter++, nPolyIndex++ ) + { + if( !(*aTickIter).bPaintIt ) + continue; + + aGridLinePoints.update( (*aTickIter).fScaledTickValue ); + addLine3D( aPoints, nPolyIndex, aGridLinePoints, m_pPosHelper->getTransformationScaledLogicToScene() ); + nRealPointCount+=3; + } + aPoints.SequenceX.realloc(nRealPointCount); + aPoints.SequenceY.realloc(nRealPointCount); + aPoints.SequenceZ.realloc(nRealPointCount); + m_pShapeFactory->createLine3D( xTarget, aPoints, aLinePropertiesList[nDepth] ); + } + } +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VCartesianGrid.hxx b/chart2/source/view/axes/VCartesianGrid.hxx new file mode 100644 index 000000000000..274b543900e3 --- /dev/null +++ b/chart2/source/view/axes/VCartesianGrid.hxx @@ -0,0 +1,71 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VCARTESIANGRID_HXX +#define _CHART2_VCARTESIANGRID_HXX + +#include "VAxisOrGridBase.hxx" +#include "VLineProperties.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class VCartesianGrid : public VAxisOrGridBase +{ +//------------------------------------------------------------------------- +// public methods +//------------------------------------------------------------------------- +public: + VCartesianGrid( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , const ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > >& rGridPropertiesList //main grid, subgrid, subsubgrid etc + ); + virtual ~VCartesianGrid(); + + virtual void SAL_CALL createShapes(); + + static void fillLinePropertiesFromGridModel( ::std::vector<VLineProperties>& rLinePropertiesList + , const ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > >& rGridPropertiesList ); + +private: + ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > > m_aGridPropertiesList; //main grid, subgrid, subsubgrid etc +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VCoordinateSystem.cxx b/chart2/source/view/axes/VCoordinateSystem.cxx new file mode 100644 index 000000000000..bc908acdf85a --- /dev/null +++ b/chart2/source/view/axes/VCoordinateSystem.cxx @@ -0,0 +1,598 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "VCoordinateSystem.hxx" +#include "VCartesianCoordinateSystem.hxx" +#include "VPolarCoordinateSystem.hxx" +#include "ScaleAutomatism.hxx" +#include "VSeriesPlotter.hxx" +#include "ShapeFactory.hxx" +#include "servicenames_coosystems.hxx" +#include "macros.hxx" +#include "AxisIndexDefines.hxx" +#include "ObjectIdentifier.hxx" +#include "ExplicitCategoriesProvider.hxx" +#include "AxisHelper.hxx" +#include "ContainerHelper.hxx" +#include "VAxisBase.hxx" +#include "ViewDefines.hxx" +#include "DataSeriesHelper.hxx" +#include "chartview/ExplicitValueProvider.hxx" +#include <com/sun/star/chart2/AxisType.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XDataSeriesContainer.hpp> + +// header for define DBG_ASSERT +#include <tools/debug.hxx> +#include <rtl/math.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; + +//static +VCoordinateSystem* VCoordinateSystem::createCoordinateSystem( + const Reference< XCoordinateSystem >& xCooSysModel ) +{ + if( !xCooSysModel.is() ) + return 0; + + rtl::OUString aViewServiceName = xCooSysModel->getViewServiceName(); + + //@todo: in future the coordinatesystems should be instanciated via service factory + VCoordinateSystem* pRet=NULL; + if( aViewServiceName.equals( CHART2_COOSYSTEM_CARTESIAN_VIEW_SERVICE_NAME ) ) + pRet = new VCartesianCoordinateSystem(xCooSysModel); + else if( aViewServiceName.equals( CHART2_COOSYSTEM_POLAR_VIEW_SERVICE_NAME ) ) + pRet = new VPolarCoordinateSystem(xCooSysModel); + if(!pRet) + pRet = new VCoordinateSystem(xCooSysModel); + return pRet; +} + +VCoordinateSystem::VCoordinateSystem( const Reference< XCoordinateSystem >& xCooSys ) + : m_xCooSysModel(xCooSys) + , m_xLogicTargetForGrids(0) + , m_xLogicTargetForAxes(0) + , m_xFinalTarget(0) + , m_xShapeFactory(0) + , m_aMatrixSceneToScreen() + , m_eLeftWallPos(CuboidPlanePosition_Left) + , m_eBackWallPos(CuboidPlanePosition_Back) + , m_eBottomPos(CuboidPlanePosition_Bottom) + , m_aMergedMinimumAndMaximumSupplier() + , m_aExplicitScales(3) + , m_aExplicitIncrements(3) + , m_apExplicitCategoriesProvider(NULL) +{ + if( !m_xCooSysModel.is() || m_xCooSysModel->getDimension()<3 ) + { + m_aExplicitScales[2].Minimum = -0.5; + m_aExplicitScales[2].Maximum = 0.5; + m_aExplicitScales[2].Orientation = AxisOrientation_MATHEMATICAL; + } +} +VCoordinateSystem::~VCoordinateSystem() +{ +} + +void SAL_CALL VCoordinateSystem::initPlottingTargets( const Reference< drawing::XShapes >& xLogicTarget + , const Reference< drawing::XShapes >& xFinalTarget + , const Reference< lang::XMultiServiceFactory >& xShapeFactory + , Reference< drawing::XShapes >& xLogicTargetForSeriesBehindAxis ) + throw (uno::RuntimeException) +{ + DBG_ASSERT(xLogicTarget.is()&&xFinalTarget.is()&&xShapeFactory.is(),"no proper initialization parameters"); + //is only allowed to be called once + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + //create group shape for grids first thus axes are always painted above grids + ShapeFactory aShapeFactory(xShapeFactory); + if(nDimensionCount==2) + { + //create and add to target + m_xLogicTargetForGrids = aShapeFactory.createGroup2D( xLogicTarget ); + xLogicTargetForSeriesBehindAxis = aShapeFactory.createGroup2D( xLogicTarget ); + m_xLogicTargetForAxes = aShapeFactory.createGroup2D( xLogicTarget ); + } + else + { + //create and added to target + m_xLogicTargetForGrids = aShapeFactory.createGroup3D( xLogicTarget ); + xLogicTargetForSeriesBehindAxis = aShapeFactory.createGroup3D( xLogicTarget ); + m_xLogicTargetForAxes = aShapeFactory.createGroup3D( xLogicTarget ); + } + m_xFinalTarget = xFinalTarget; + m_xShapeFactory = xShapeFactory; +} + +void VCoordinateSystem::setParticle( const rtl::OUString& rCooSysParticle ) +{ + m_aCooSysParticle = rCooSysParticle; +} + +void VCoordinateSystem::setTransformationSceneToScreen( + const drawing::HomogenMatrix& rMatrix ) +{ + m_aMatrixSceneToScreen = rMatrix; + + //correct transformation for axis + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + if(2==pVAxis->getDimensionCount()) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + } + } +} + +drawing::HomogenMatrix VCoordinateSystem::getTransformationSceneToScreen() +{ + return m_aMatrixSceneToScreen; +} + +//better performance for big data +uno::Sequence< sal_Int32 > VCoordinateSystem::getCoordinateSystemResolution( + const awt::Size& rPageSize, const awt::Size& rPageResolution ) +{ + uno::Sequence< sal_Int32 > aResolution(2); + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + if(nDimensionCount>2) + aResolution.realloc(nDimensionCount); + sal_Int32 nN = 0; + for( nN = 0 ;nN<aResolution.getLength(); nN++ ) + aResolution[nN]=1000; + + ::basegfx::B3DTuple aScale( BaseGFXHelper::GetScaleFromMatrix( + BaseGFXHelper::HomogenMatrixToB3DHomMatrix( + m_aMatrixSceneToScreen ) ) ); + + double fCoosysWidth = static_cast< double >( fabs(aScale.getX()*FIXED_SIZE_FOR_3D_CHART_VOLUME)); + double fCoosysHeight = static_cast< double >( fabs(aScale.getY()*FIXED_SIZE_FOR_3D_CHART_VOLUME)); + + double fPageWidth = rPageSize.Width; + double fPageHeight = rPageSize.Height; + + //factor 2 to avoid rounding problems + sal_Int32 nXResolution = static_cast<sal_Int32>(2.0*static_cast<double>(rPageResolution.Width)*fCoosysWidth/fPageWidth); + sal_Int32 nYResolution = static_cast<sal_Int32>(2.0*static_cast<double>(rPageResolution.Height)*fCoosysHeight/fPageHeight); + + if( nXResolution < 10 ) + nXResolution = 10; + if( nYResolution < 10 ) + nYResolution = 10; + + if( this->getPropertySwapXAndYAxis() ) + std::swap(nXResolution,nYResolution); + + //2D + if( 2 == aResolution.getLength() ) + { + aResolution[0]=nXResolution; + aResolution[1]=nYResolution; + } + else + { + //this maybe can be optimized further ... + sal_Int32 nMaxResolution = std::max( nXResolution, nYResolution ); + nMaxResolution*=2; + for( nN = 0 ;nN<aResolution.getLength(); nN++ ) + aResolution[nN]=nMaxResolution; + } + + return aResolution; +} + +Reference< XCoordinateSystem > VCoordinateSystem::getModel() const +{ + return m_xCooSysModel; +} + +Reference< XAxis > VCoordinateSystem::getAxisByDimension( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const +{ + if( m_xCooSysModel.is() ) + return m_xCooSysModel->getAxisByDimension( nDimensionIndex, nAxisIndex ); + return 0; +} + +Sequence< Reference< beans::XPropertySet > > VCoordinateSystem::getGridListFromAxis( const Reference< XAxis >& xAxis ) +{ + std::vector< Reference< beans::XPropertySet > > aRet; + + if( xAxis.is() ) + { + aRet.push_back( xAxis->getGridProperties() ); + std::vector< Reference< beans::XPropertySet > > aSubGrids( ContainerHelper::SequenceToVector( xAxis->getSubGridProperties() ) ); + aRet.insert( aRet.end(), aSubGrids.begin(), aSubGrids.end() ); + } + + return ContainerHelper::ContainerToSequence( aRet ); +} + +void VCoordinateSystem::impl_adjustDimension( sal_Int32& rDimensionIndex ) const +{ + if( rDimensionIndex<0 ) + rDimensionIndex=0; + if( rDimensionIndex>2 ) + rDimensionIndex=2; +} + +void VCoordinateSystem::impl_adjustDimensionAndIndex( sal_Int32& rDimensionIndex, sal_Int32& rAxisIndex ) const +{ + impl_adjustDimension( rDimensionIndex ); + + if( rAxisIndex < 0 || rAxisIndex > this->getMaximumAxisIndexByDimension(rDimensionIndex) ) + rAxisIndex = 0; +} + +void VCoordinateSystem::setExplicitCategoriesProvider( ExplicitCategoriesProvider* pExplicitCategoriesProvider /*takes ownership*/ ) +{ + m_apExplicitCategoriesProvider = ::std::auto_ptr< ExplicitCategoriesProvider >(pExplicitCategoriesProvider); +} + +ExplicitCategoriesProvider* VCoordinateSystem::getExplicitCategoriesProvider() +{ + return m_apExplicitCategoriesProvider.get(); +} + +Sequence< ExplicitScaleData > VCoordinateSystem::getExplicitScales( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const +{ + Sequence< ExplicitScaleData > aRet(m_aExplicitScales); + + impl_adjustDimensionAndIndex( nDimensionIndex, nAxisIndex ); + aRet[nDimensionIndex]=this->getExplicitScale( nDimensionIndex, nAxisIndex ); + + return aRet; +} + +Sequence< ExplicitIncrementData > VCoordinateSystem::getExplicitIncrements( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const +{ + Sequence< ExplicitIncrementData > aRet(m_aExplicitIncrements); + + impl_adjustDimensionAndIndex( nDimensionIndex, nAxisIndex ); + aRet[nDimensionIndex]=this->getExplicitIncrement( nDimensionIndex, nAxisIndex ); + + return aRet; +} + +ExplicitScaleData VCoordinateSystem::getExplicitScale( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const +{ + ExplicitScaleData aRet; + + impl_adjustDimensionAndIndex( nDimensionIndex, nAxisIndex ); + + if( nAxisIndex == 0) + { + aRet = m_aExplicitScales[nDimensionIndex]; + } + else + { + tFullAxisIndex aFullAxisIndex( nDimensionIndex, nAxisIndex ); + tFullExplicitScaleMap::const_iterator aIt = m_aSecondaryExplicitScales.find( aFullAxisIndex ); + if( aIt != m_aSecondaryExplicitScales.end() ) + aRet = aIt->second; + else + aRet = m_aExplicitScales[nDimensionIndex]; + } + + return aRet; +} + +ExplicitIncrementData VCoordinateSystem::getExplicitIncrement( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) const +{ + ExplicitIncrementData aRet; + + impl_adjustDimensionAndIndex( nDimensionIndex, nAxisIndex ); + + if( nAxisIndex == 0) + { + aRet = m_aExplicitIncrements[nDimensionIndex]; + } + else + { + tFullAxisIndex aFullAxisIndex( nDimensionIndex, nAxisIndex ); + tFullExplicitIncrementMap::const_iterator aIt = m_aSecondaryExplicitIncrements.find( aFullAxisIndex ); + if( aIt != m_aSecondaryExplicitIncrements.end() ) + aRet = aIt->second; + else + aRet = m_aExplicitIncrements[nDimensionIndex]; + } + + return aRet; +} + +rtl::OUString VCoordinateSystem::createCIDForAxis( const Reference< chart2::XAxis >& /* xAxis */, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) +{ + rtl::OUString aAxisParticle( ObjectIdentifier::createParticleForAxis( nDimensionIndex, nAxisIndex ) ); + return ObjectIdentifier::createClassifiedIdentifierForParticles( m_aCooSysParticle, aAxisParticle ); +} +rtl::OUString VCoordinateSystem::createCIDForGrid( const Reference< chart2::XAxis >& /* xAxis */, sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) +{ + rtl::OUString aGridParticle( ObjectIdentifier::createParticleForGrid( nDimensionIndex, nAxisIndex ) ); + return ObjectIdentifier::createClassifiedIdentifierForParticles( m_aCooSysParticle, aGridParticle ); +} + +sal_Int32 VCoordinateSystem::getMaximumAxisIndexByDimension( sal_Int32 nDimensionIndex ) const +{ + sal_Int32 nRet = 0; + tFullExplicitScaleMap::const_iterator aIt = m_aSecondaryExplicitScales.begin(); + tFullExplicitScaleMap::const_iterator aEnd = m_aSecondaryExplicitScales.end(); + for(; aIt!=aEnd; ++aIt) + { + if(aIt->first.first==nDimensionIndex) + { + sal_Int32 nLocalIdx = aIt->first.second; + if( nRet < nLocalIdx ) + nRet = nLocalIdx; + } + } + return nRet; +} + +void VCoordinateSystem::createVAxisList( + const uno::Reference< util::XNumberFormatsSupplier > & /* xNumberFormatsSupplier */ + , const awt::Size& /* rFontReferenceSize */ + , const awt::Rectangle& /* rMaximumSpaceForLabels */ + ) +{ +} + +void VCoordinateSystem::initVAxisInList() +{ +} +void VCoordinateSystem::updateScalesAndIncrementsOnAxes() +{ +} + +void VCoordinateSystem::prepareScaleAutomatismForDimensionAndIndex( ScaleAutomatism& rScaleAutomatism, sal_Int32 nDimIndex, sal_Int32 nAxisIndex ) +{ + double fMin = 0.0; + double fMax = 0.0; + ::rtl::math::setInf(&fMin, false); + ::rtl::math::setInf(&fMax, true); + if( 0 == nDimIndex ) + { + fMin = m_aMergedMinimumAndMaximumSupplier.getMinimumX(); + fMax = m_aMergedMinimumAndMaximumSupplier.getMaximumX(); + } + else if( 1 == nDimIndex ) + { + ExplicitScaleData aScale = getExplicitScale( 0, 0 ); + fMin = m_aMergedMinimumAndMaximumSupplier.getMinimumYInRange(aScale.Minimum,aScale.Maximum, nAxisIndex); + fMax = m_aMergedMinimumAndMaximumSupplier.getMaximumYInRange(aScale.Minimum,aScale.Maximum, nAxisIndex); + } + else if( 2 == nDimIndex ) + { + fMin = m_aMergedMinimumAndMaximumSupplier.getMinimumZ(); + fMax = m_aMergedMinimumAndMaximumSupplier.getMaximumZ(); + } + + this->prepareScaleAutomatism( rScaleAutomatism, fMin, fMax, nDimIndex, nAxisIndex ); +} + +void VCoordinateSystem::prepareScaleAutomatism( ScaleAutomatism& rScaleAutomatism, double fMin, double fMax, sal_Int32 nDimIndex, sal_Int32 nAxisIndex ) +{ + //merge our values with those already contained in rScaleAutomatism + rScaleAutomatism.expandValueRange( fMin, fMax ); + + rScaleAutomatism.setAutoScalingOptions( + m_aMergedMinimumAndMaximumSupplier.isExpandBorderToIncrementRhythm( nDimIndex ), + m_aMergedMinimumAndMaximumSupplier.isExpandIfValuesCloseToBorder( nDimIndex ), + m_aMergedMinimumAndMaximumSupplier.isExpandWideValuesToZero( nDimIndex ), + m_aMergedMinimumAndMaximumSupplier.isExpandNarrowValuesTowardZero( nDimIndex ) ); + + VAxisBase* pVAxis( this->getVAxis( nDimIndex, nAxisIndex ) ); + if( pVAxis ) + rScaleAutomatism.setMaximumAutoMainIncrementCount( pVAxis->estimateMaximumAutoMainIncrementCount() ); +} + +VAxisBase* VCoordinateSystem::getVAxis( sal_Int32 nDimensionIndex, sal_Int32 nAxisIndex ) +{ + VAxisBase* pRet = 0; + + tFullAxisIndex aFullAxisIndex( nDimensionIndex, nAxisIndex ); + + tVAxisMap::const_iterator aIt = m_aAxisMap.find( aFullAxisIndex ); + if( aIt != m_aAxisMap.end() ) + pRet = aIt->second.get(); + + return pRet; +} + +void VCoordinateSystem::setExplicitScaleAndIncrement( + sal_Int32 nDimensionIndex + , sal_Int32 nAxisIndex + , const ExplicitScaleData& rExplicitScale + , const ExplicitIncrementData& rExplicitIncrement ) +{ + impl_adjustDimension( nDimensionIndex ); + + if( nAxisIndex==0 ) + { + m_aExplicitScales[nDimensionIndex]=rExplicitScale; + m_aExplicitIncrements[nDimensionIndex]=rExplicitIncrement; + } + else + { + tFullAxisIndex aFullAxisIndex( nDimensionIndex, nAxisIndex ); + m_aSecondaryExplicitScales[aFullAxisIndex] = rExplicitScale; + m_aSecondaryExplicitIncrements[aFullAxisIndex] = rExplicitIncrement; + } +} + +void VCoordinateSystem::set3DWallPositions( CuboidPlanePosition eLeftWallPos, CuboidPlanePosition eBackWallPos, CuboidPlanePosition eBottomPos ) +{ + m_eLeftWallPos = eLeftWallPos; + m_eBackWallPos = eBackWallPos; + m_eBottomPos = eBottomPos; +} + +void VCoordinateSystem::createMaximumAxesLabels() +{ + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + if(2==pVAxis->getDimensionCount()) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + pVAxis->createMaximumLabels(); + } + } +} +void VCoordinateSystem::createAxesLabels() +{ + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + if(2==pVAxis->getDimensionCount()) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + pVAxis->createLabels(); + } + } +} + +void VCoordinateSystem::updatePositions() +{ + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + if(2==pVAxis->getDimensionCount()) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + pVAxis->updatePositions(); + } + } +} + +void VCoordinateSystem::createAxesShapes() +{ + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + if(2==pVAxis->getDimensionCount()) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + + tFullAxisIndex aFullAxisIndex = aIt->first; + if( aFullAxisIndex.second == 0 ) + { + if( aFullAxisIndex.first == 0 ) + { + if( AxisType::CATEGORY!=m_aExplicitScales[1].AxisType ) + pVAxis->setExrtaLinePositionAtOtherAxis( + m_aExplicitScales[1].Origin ); + } + else if( aFullAxisIndex.first == 1 ) + { + if( AxisType::CATEGORY!=m_aExplicitScales[0].AxisType ) + pVAxis->setExrtaLinePositionAtOtherAxis( + m_aExplicitScales[0].Origin ); + } + } + + pVAxis->createShapes(); + } + } +} +void VCoordinateSystem::createGridShapes() +{ +} +void VCoordinateSystem::addMinimumAndMaximumSupplier( MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier ) +{ + m_aMergedMinimumAndMaximumSupplier.addMinimumAndMaximumSupplier(pMinimumAndMaximumSupplier); +} + +bool VCoordinateSystem::hasMinimumAndMaximumSupplier( MinimumAndMaximumSupplier* pMinimumAndMaximumSupplier ) +{ + return m_aMergedMinimumAndMaximumSupplier.hasMinimumAndMaximumSupplier(pMinimumAndMaximumSupplier); +} + +void VCoordinateSystem::clearMinimumAndMaximumSupplierList() +{ + m_aMergedMinimumAndMaximumSupplier.clearMinimumAndMaximumSupplierList(); +} + +bool VCoordinateSystem::getPropertySwapXAndYAxis() const +{ + Reference<beans::XPropertySet> xProp(m_xCooSysModel, uno::UNO_QUERY ); + sal_Bool bSwapXAndY = false; + if( xProp.is()) try + { + xProp->getPropertyValue( C2U( "SwapXAndYAxis" ) ) >>= bSwapXAndY; + } + catch( uno::Exception& e ) + { + ASSERT_EXCEPTION( e ); + } + return bSwapXAndY; +} + +bool VCoordinateSystem::needSeriesNamesForAxis() const +{ + return ( m_xCooSysModel.is() && m_xCooSysModel->getDimension() == 3 ); +} +void VCoordinateSystem::setSeriesNamesForAxis( const Sequence< rtl::OUString >& rSeriesNames ) +{ + m_aSeriesNamesForZAxis = rSeriesNames; +} + +sal_Int32 VCoordinateSystem::getNumberFormatKeyForAxis( + const Reference< chart2::XAxis >& xAxis + , const Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier ) +{ + return ExplicitValueProvider::getExplicitNumberFormatKeyForAxis( + xAxis, m_xCooSysModel, xNumberFormatsSupplier ); +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VPolarAngleAxis.cxx b/chart2/source/view/axes/VPolarAngleAxis.cxx new file mode 100644 index 000000000000..865a7848aa9a --- /dev/null +++ b/chart2/source/view/axes/VPolarAngleAxis.cxx @@ -0,0 +1,234 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include <basegfx/numeric/ftools.hxx> + +#include "VPolarAngleAxis.hxx" +#include "VPolarGrid.hxx" +#include "ShapeFactory.hxx" +#include "macros.hxx" +#include "chartview/NumberFormatterWrapper.hxx" +#include "PolarLabelPositionHelper.hxx" +#include <tools/color.hxx> + +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using namespace ::rtl::math; + +VPolarAngleAxis::VPolarAngleAxis( const AxisProperties& rAxisProperties + , const uno::Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionCount ) + : VPolarAxis( rAxisProperties, xNumberFormatsSupplier, 0/*nDimensionIndex*/, nDimensionCount ) +{ +} + +VPolarAngleAxis::~VPolarAngleAxis() +{ + delete m_pPosHelper; + m_pPosHelper = NULL; +} + +bool VPolarAngleAxis::createTextShapes_ForAngleAxis( + const uno::Reference< drawing::XShapes >& xTarget + , EquidistantTickIter& rTickIter + , AxisLabelProperties& rAxisLabelProperties + , double fLogicRadius + , double fLogicZ ) +{ + sal_Int32 nDimensionCount = 2; + ShapeFactory aShapeFactory(m_xShapeFactory); + + FixedNumberFormatter aFixedNumberFormatter( + m_xNumberFormatsSupplier, rAxisLabelProperties.nNumberFormatKey ); + + //------------------------------------------------ + //prepare text properties for multipropertyset-interface of shape + tNameSequence aPropNames; + tAnySequence aPropValues; + + uno::Reference< beans::XPropertySet > xProps( m_aAxisProperties.m_xAxisModel, uno::UNO_QUERY ); + PropertyMapper::getTextLabelMultiPropertyLists( xProps, aPropNames, aPropValues, false ); + LabelPositionHelper::doDynamicFontResize( aPropValues, aPropNames, xProps + , rAxisLabelProperties.m_aFontReferenceSize ); + + uno::Any* pColorAny = PropertyMapper::getValuePointer(aPropValues,aPropNames,C2U("CharColor")); + sal_Int32 nColor = Color( COL_AUTO ).GetColor(); + if(pColorAny) + *pColorAny >>= nColor; + + const uno::Sequence< rtl::OUString >* pLabels = m_bUseTextLabels? &m_aTextLabels : 0; + + //------------------------------------------------ + + //TickInfo* pLastVisibleNeighbourTickInfo = NULL; + sal_Int32 nTick = 0; + + for( TickInfo* pTickInfo = rTickIter.firstInfo() + ; pTickInfo + ; pTickInfo = rTickIter.nextInfo(), nTick++ ) + { + //don't create labels which does not fit into the rhythm + if( nTick%rAxisLabelProperties.nRhythm != 0) + continue; + + //don't create labels for invisible ticks + if( !pTickInfo->bPaintIt ) + continue; + + //if NO OVERLAP -> don't create labels where the + //anchor position is the same as for the last label + //@todo + + if(!pTickInfo->xTextShape.is()) + { + //create single label + bool bHasExtraColor=false; + sal_Int32 nExtraColor=0; + + rtl::OUString aLabel; + if(pLabels) + { + sal_Int32 nIndex = static_cast< sal_Int32 >(pTickInfo->fUnscaledTickValue) - 1; //first category (index 0) matches with real number 1.0 + if( nIndex>=0 && nIndex<pLabels->getLength() ) + aLabel = (*pLabels)[nIndex]; + } + else + aLabel = aFixedNumberFormatter.getFormattedString( pTickInfo->fUnscaledTickValue, nExtraColor, bHasExtraColor ); + + if(pColorAny) + *pColorAny = uno::makeAny(bHasExtraColor?nExtraColor:nColor); + + double fLogicAngle = pTickInfo->fUnscaledTickValue; + + LabelAlignment eLabelAlignment(LABEL_ALIGN_CENTER); + PolarLabelPositionHelper aPolarLabelPositionHelper(m_pPosHelper,nDimensionCount,xTarget,&aShapeFactory); + sal_Int32 nScreenValueOffsetInRadiusDirection = m_aAxisLabelProperties.m_aMaximumSpaceForLabels.Height/15; + awt::Point aAnchorScreenPosition2D( aPolarLabelPositionHelper.getLabelScreenPositionAndAlignmentForLogicValues( + eLabelAlignment, fLogicAngle, fLogicRadius, fLogicZ, nScreenValueOffsetInRadiusDirection )); + LabelPositionHelper::changeTextAdjustment( aPropValues, aPropNames, eLabelAlignment ); + + // #i78696# use mathematically correct rotation now + const double fRotationAnglePi(rAxisLabelProperties.fRotationAngleDegree * (F_PI / -180.0)); + + uno::Any aATransformation = ShapeFactory::makeTransformation( aAnchorScreenPosition2D, fRotationAnglePi ); + rtl::OUString aStackedLabel = ShapeFactory::getStackedString( aLabel, rAxisLabelProperties.bStackCharacters ); + + pTickInfo->xTextShape = aShapeFactory.createText( xTarget, aStackedLabel, aPropNames, aPropValues, aATransformation ); + } + + //if NO OVERLAP -> remove overlapping shapes + //@todo + } + return true; +} + +void SAL_CALL VPolarAngleAxis::createMaximumLabels() +{ + if( !prepareShapeCreation() ) + return; + + createLabels(); +} + +void SAL_CALL VPolarAngleAxis::updatePositions() +{ + //todo: really only update the positions + + if( !prepareShapeCreation() ) + return; + + createLabels(); +} + +void SAL_CALL VPolarAngleAxis::createLabels() +{ + if( !prepareShapeCreation() ) + return; + + double fLogicRadius = m_pPosHelper->getOuterLogicRadius(); + double fLogicZ = -0.5;//as defined + + if( m_aAxisProperties.m_bDisplayLabels ) + { + //----------------------------------------- + //get the transformed screen values for all tickmarks in aAllTickInfos + std::auto_ptr< TickmarkHelper > apTickmarkHelper( this->createTickmarkHelper() ); + + //create tick mark text shapes + //@todo: iterate through all tick depth wich should be labeled + + EquidistantTickIter aTickIter( m_aAllTickInfos, m_aIncrement, 0, 0 ); + this->updateUnscaledValuesAtTicks( aTickIter ); + + removeTextShapesFromTicks(); + + AxisLabelProperties aAxisLabelProperties( m_aAxisLabelProperties ); + aAxisLabelProperties.bOverlapAllowed = true; + while( !createTextShapes_ForAngleAxis( m_xTextTarget, aTickIter + , aAxisLabelProperties + , fLogicRadius, fLogicZ + ) ) + { + }; + + //no staggering for polar angle axis + } +} + +void SAL_CALL VPolarAngleAxis::createShapes() +{ + if( !prepareShapeCreation() ) + return; + + double fLogicRadius = m_pPosHelper->getOuterLogicRadius(); + double fLogicZ = -0.5;//as defined + + //----------------------------------------- + //create axis main lines + drawing::PointSequenceSequence aPoints(1); + VPolarGrid::createLinePointSequence_ForAngleAxis( aPoints, m_aAllTickInfos, m_aIncrement, m_aScale, m_pPosHelper, fLogicRadius, fLogicZ ); + uno::Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( + m_xGroupShape_Shapes, aPoints, &m_aAxisProperties.m_aLineProperties ); + //because of this name this line will be used for marking the axis + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + + //----------------------------------------- + //create labels + createLabels(); +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VPolarAngleAxis.hxx b/chart2/source/view/axes/VPolarAngleAxis.hxx new file mode 100644 index 000000000000..c03cf533e22c --- /dev/null +++ b/chart2/source/view/axes/VPolarAngleAxis.hxx @@ -0,0 +1,66 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VPOLARANGLEAXIS_HXX +#define _CHART2_VPOLARANGLEAXIS_HXX + +#include "VPolarAxis.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class VPolarAngleAxis : public VPolarAxis +{ +public: + VPolarAngleAxis( const AxisProperties& rAxisProperties + , const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionCount ); + virtual ~VPolarAngleAxis(); + + virtual void SAL_CALL createMaximumLabels(); + virtual void SAL_CALL createLabels(); + virtual void SAL_CALL updatePositions(); + + virtual void SAL_CALL createShapes(); + +private: //methods + bool createTextShapes_ForAngleAxis( + const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes >& xTarget + , EquidistantTickIter& rTickIter + , AxisLabelProperties& rAxisLabelProperties + , double fLogicRadius, double fLogicZ ); +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VPolarAxis.cxx b/chart2/source/view/axes/VPolarAxis.cxx new file mode 100644 index 000000000000..23908585ec3a --- /dev/null +++ b/chart2/source/view/axes/VPolarAxis.cxx @@ -0,0 +1,83 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "VPolarAxis.hxx" +#include "VPolarAngleAxis.hxx" +#include "VPolarRadiusAxis.hxx" +#include "macros.hxx" +#include "TickmarkHelper.hxx" +#include "ShapeFactory.hxx" + +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; + +VPolarAxis* VPolarAxis::createAxis( const AxisProperties& rAxisProperties + , const uno::Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount ) +{ + if( 0==nDimensionIndex ) + return new VPolarAngleAxis( rAxisProperties, xNumberFormatsSupplier, nDimensionCount ); + return new VPolarRadiusAxis( rAxisProperties, xNumberFormatsSupplier, nDimensionCount ); +} + +VPolarAxis::VPolarAxis( const AxisProperties& rAxisProperties + , const uno::Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount ) + : VAxisBase( nDimensionIndex, nDimensionCount, rAxisProperties, xNumberFormatsSupplier ) + , m_pPosHelper( new PolarPlottingPositionHelper() ) + , m_aIncrements() +{ + PlotterBase::m_pPosHelper = m_pPosHelper; +} + +VPolarAxis::~VPolarAxis() +{ + delete m_pPosHelper; + m_pPosHelper = NULL; +} + +void VPolarAxis::setIncrements( const uno::Sequence< ExplicitIncrementData >& rIncrements ) +{ + m_aIncrements = rIncrements; +} + +sal_Bool SAL_CALL VPolarAxis::isAnythingToDraw() +{ + return ( 2==m_nDimension && VAxisBase::isAnythingToDraw() ); +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VPolarAxis.hxx b/chart2/source/view/axes/VPolarAxis.hxx new file mode 100644 index 000000000000..8d4c14188258 --- /dev/null +++ b/chart2/source/view/axes/VPolarAxis.hxx @@ -0,0 +1,71 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VPOLARAXIS_HXX +#define _CHART2_VPOLARAXIS_HXX + +#include "VAxisBase.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class PolarPlottingPositionHelper; + +class VPolarAxis : public VAxisBase +{ +public: + static VPolarAxis* createAxis( const AxisProperties& rAxisProperties + , const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount ); + + void setIncrements( const ::com::sun::star::uno::Sequence< + ::com::sun::star::chart2::ExplicitIncrementData >& rIncrements ); + + virtual sal_Bool SAL_CALL isAnythingToDraw(); + + virtual ~VPolarAxis(); + +protected: + VPolarAxis( const AxisProperties& rAxisProperties + , const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount ); + +protected: //member + PolarPlottingPositionHelper* m_pPosHelper; + ::com::sun::star::uno::Sequence< + ::com::sun::star::chart2::ExplicitIncrementData > m_aIncrements; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VPolarCoordinateSystem.cxx b/chart2/source/view/axes/VPolarCoordinateSystem.cxx new file mode 100644 index 000000000000..76e057a2b32d --- /dev/null +++ b/chart2/source/view/axes/VPolarCoordinateSystem.cxx @@ -0,0 +1,211 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "VPolarCoordinateSystem.hxx" +#include "VPolarGrid.hxx" +#include "VPolarAxis.hxx" +#include "AxisIndexDefines.hxx" +#include "AxisHelper.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; + +VPolarCoordinateSystem::VPolarCoordinateSystem( const Reference< XCoordinateSystem >& xCooSys ) + : VCoordinateSystem(xCooSys) +{ +} + +VPolarCoordinateSystem::~VPolarCoordinateSystem() +{ +} + +//better performance for big data +uno::Sequence< sal_Int32 > VPolarCoordinateSystem::getCoordinateSystemResolution( + const awt::Size& rPageSize, const awt::Size& rPageResolution ) +{ + uno::Sequence< sal_Int32 > aResolution( VCoordinateSystem::getCoordinateSystemResolution( rPageSize, rPageResolution) ); + + if( aResolution.getLength() >= 2 ) + { + if( this->getPropertySwapXAndYAxis() ) + { + aResolution[0]/=2;//radius + aResolution[1]*=4;//outer circle resolution + } + else + { + aResolution[0]*=4;//outer circle resolution + aResolution[1]/=2;//radius + } + } + + return aResolution; +} + +void VPolarCoordinateSystem::createVAxisList( + const uno::Reference< util::XNumberFormatsSupplier > & xNumberFormatsSupplier + , const awt::Size& rFontReferenceSize + , const awt::Rectangle& rMaximumSpaceForLabels + ) +{ + m_aAxisMap.clear(); + + //if(!m_xLogicTargetForAxes.is() || !m_xFinalTarget.is() || !m_xCooSysModel.is() ) + // return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); +// bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + sal_Int32 nDimensionIndex = 0; + + //create angle axis (dimension index 0) + for( nDimensionIndex = 0; nDimensionIndex < nDimensionCount; nDimensionIndex++ ) + { + sal_Int32 nMaxAxisIndex = m_xCooSysModel->getMaximumAxisIndexByDimension(nDimensionIndex); + for( sal_Int32 nAxisIndex = 0; nAxisIndex <= nMaxAxisIndex; nAxisIndex++ ) + { + Reference< XAxis > xAxis( this->getAxisByDimension(nDimensionIndex,nAxisIndex) ); + if(!xAxis.is() || !AxisHelper::shouldAxisBeDisplayed( xAxis, m_xCooSysModel )) + continue; + AxisProperties aAxisProperties(xAxis,this->getExplicitCategoriesProvider()); + aAxisProperties.init(); + if(aAxisProperties.m_bDisplayLabels) + aAxisProperties.m_nNumberFormatKey = this->getNumberFormatKeyForAxis( xAxis, xNumberFormatsSupplier ); + //------------------- + ::boost::shared_ptr< VAxisBase > apVAxis( VPolarAxis::createAxis( aAxisProperties,xNumberFormatsSupplier,nDimensionIndex,nDimensionCount) ); + tFullAxisIndex aFullAxisIndex( nDimensionIndex, nAxisIndex ); + m_aAxisMap[aFullAxisIndex] = apVAxis; + + //apVAxis->setExplicitScaleAndIncrement( this->getExplicitScale( nDimensionIndex, nAxisIndex ), this->getExplicitIncrement(nDimensionIndex, nAxisIndex) ); + //apVAxis->initPlotter(m_xLogicTargetForAxes,m_xFinalTarget,m_xShapeFactory + // , this->createCIDForAxis( xAxis, nDimensionIndex, nAxisIndex ) ); + //VPolarAxis* pVPolarAxis = dynamic_cast< VPolarAxis* >( apVAxis.get() ); + //if( pVPolarAxis ) + // pVPolarAxis->setIncrements( this->getExplicitIncrements( nDimensionIndex, nAxisIndex ) ); + //if(2==nDimensionCount) + // apVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + //apVAxis->setScales( this->getExplicitScales( nDimensionIndex, nAxisIndex ), bSwapXAndY ); + apVAxis->initAxisLabelProperties(rFontReferenceSize,rMaximumSpaceForLabels); + } + } +} + +void VPolarCoordinateSystem::initVAxisInList() +{ + if(!m_xLogicTargetForAxes.is() || !m_xFinalTarget.is() || !m_xCooSysModel.is() ) + return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + sal_Int32 nDimensionIndex = aIt->first.first; + sal_Int32 nAxisIndex = aIt->first.second; + pVAxis->setExplicitScaleAndIncrement( this->getExplicitScale( nDimensionIndex, nAxisIndex ), this->getExplicitIncrement(nDimensionIndex, nAxisIndex) ); + pVAxis->initPlotter(m_xLogicTargetForAxes,m_xFinalTarget,m_xShapeFactory + , this->createCIDForAxis( getAxisByDimension( nDimensionIndex, nAxisIndex ), nDimensionIndex, nAxisIndex ) ); + VPolarAxis* pVPolarAxis = dynamic_cast< VPolarAxis* >( pVAxis ); + if( pVPolarAxis ) + pVPolarAxis->setIncrements( this->getExplicitIncrements( nDimensionIndex, nAxisIndex ) ); + if(2==nDimensionCount) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + pVAxis->setScales( this->getExplicitScales( nDimensionIndex, nAxisIndex ), bSwapXAndY ); + } + } +} + +void VPolarCoordinateSystem::updateScalesAndIncrementsOnAxes() +{ + if(!m_xLogicTargetForAxes.is() || !m_xFinalTarget.is() || !m_xCooSysModel.is() ) + return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + tVAxisMap::iterator aIt( m_aAxisMap.begin() ); + tVAxisMap::const_iterator aEnd( m_aAxisMap.end() ); + for( ; aIt != aEnd; ++aIt ) + { + VAxisBase* pVAxis = aIt->second.get(); + if( pVAxis ) + { + sal_Int32 nDimensionIndex = aIt->first.first; + sal_Int32 nAxisIndex = aIt->first.second; + pVAxis->setExplicitScaleAndIncrement( this->getExplicitScale( nDimensionIndex, nAxisIndex ), this->getExplicitIncrement(nDimensionIndex, nAxisIndex) ); + VPolarAxis* pVPolarAxis = dynamic_cast< VPolarAxis* >( pVAxis ); + if( pVPolarAxis ) + pVPolarAxis->setIncrements( this->getExplicitIncrements( nDimensionIndex, nAxisIndex ) ); + if(2==nDimensionCount) + pVAxis->setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + pVAxis->setScales( this->getExplicitScales( nDimensionIndex, nAxisIndex ), bSwapXAndY ); + } + } +} + +void VPolarCoordinateSystem::createGridShapes() +{ + if(!m_xLogicTargetForGrids.is() || !m_xFinalTarget.is() ) + return; + + sal_Int32 nDimensionCount = m_xCooSysModel->getDimension(); + bool bSwapXAndY = this->getPropertySwapXAndYAxis(); + + for( sal_Int32 nDimensionIndex=0; nDimensionIndex<3; nDimensionIndex++) + { + sal_Int32 nAxisIndex = MAIN_AXIS_INDEX; + + Reference< XAxis > xAxis( AxisHelper::getAxis( nDimensionIndex, nAxisIndex, m_xCooSysModel ) ); + if(!xAxis.is() || !AxisHelper::shouldAxisBeDisplayed( xAxis, m_xCooSysModel )) + continue; + + VPolarGrid aGrid(nDimensionIndex,nDimensionCount,this->getGridListFromAxis( xAxis )); + aGrid.setIncrements( this->getExplicitIncrements( nDimensionIndex, nAxisIndex ) ); + aGrid.initPlotter(m_xLogicTargetForGrids,m_xFinalTarget,m_xShapeFactory + , this->createCIDForGrid( xAxis, nDimensionIndex, nAxisIndex ) ); + if(2==nDimensionCount) + aGrid.setTransformationSceneToScreen( m_aMatrixSceneToScreen ); + aGrid.setScales( this->getExplicitScales( nDimensionIndex, nAxisIndex), bSwapXAndY ); + aGrid.createShapes(); + } +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VPolarCoordinateSystem.hxx b/chart2/source/view/axes/VPolarCoordinateSystem.hxx new file mode 100644 index 000000000000..f1ba84c5469e --- /dev/null +++ b/chart2/source/view/axes/VPolarCoordinateSystem.hxx @@ -0,0 +1,68 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VPOLAR_COORDINATESYSTEM_HXX +#define _CHART2_VPOLAR_COORDINATESYSTEM_HXX + +#include "VCoordinateSystem.hxx" + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ +class VPolarCoordinateSystem : public VCoordinateSystem +{ +public: + VPolarCoordinateSystem( const ::com::sun::star::uno::Reference< + ::com::sun::star::chart2::XCoordinateSystem >& xCooSys ); + virtual ~VPolarCoordinateSystem(); + + //better performance for big data + virtual ::com::sun::star::uno::Sequence< sal_Int32 > getCoordinateSystemResolution( const ::com::sun::star::awt::Size& rPageSize + , const ::com::sun::star::awt::Size& rPageResolution ); + + virtual void createVAxisList( + const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier > & xNumberFormatsSupplier + , const ::com::sun::star::awt::Size& rFontReferenceSize + , const ::com::sun::star::awt::Rectangle& rMaximumSpaceForLabels ); + + virtual void initVAxisInList(); + virtual void updateScalesAndIncrementsOnAxes(); + + virtual void createGridShapes(); + +private: + VPolarCoordinateSystem(); +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VPolarGrid.cxx b/chart2/source/view/axes/VPolarGrid.cxx new file mode 100644 index 000000000000..0bd98b642acb --- /dev/null +++ b/chart2/source/view/axes/VPolarGrid.cxx @@ -0,0 +1,273 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" +#include "VPolarGrid.hxx" +#include "VCartesianGrid.hxx" +#include "TickmarkHelper.hxx" +#include "PlottingPositionHelper.hxx" +#include "ShapeFactory.hxx" +#include "ObjectIdentifier.hxx" +#include "macros.hxx" +#include "CommonConverters.hxx" +#include <com/sun/star/drawing/LineStyle.hpp> + +#include <vector> +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using ::com::sun::star::uno::Reference; + +VPolarGrid::VPolarGrid( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , const uno::Sequence< Reference< beans::XPropertySet > > & rGridPropertiesList ) + : VAxisOrGridBase( nDimensionIndex, nDimensionCount ) + , m_aGridPropertiesList( rGridPropertiesList ) + , m_pPosHelper( new PolarPlottingPositionHelper() ) + , m_aIncrements() +{ + PlotterBase::m_pPosHelper = m_pPosHelper; +} + +VPolarGrid::~VPolarGrid() +{ + delete m_pPosHelper; + m_pPosHelper = NULL; +} + +void VPolarGrid::setIncrements( const uno::Sequence< ExplicitIncrementData >& rIncrements ) +{ + m_aIncrements = rIncrements; +} + +void VPolarGrid::getAllTickInfos( sal_Int32 nDimensionIndex, ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const +{ + TickmarkHelper aTickmarkHelper( + m_pPosHelper->getScales()[nDimensionIndex], m_aIncrements[nDimensionIndex] ); + aTickmarkHelper.getAllTicks( rAllTickInfos ); +} + +//static +void VPolarGrid::createLinePointSequence_ForAngleAxis( + drawing::PointSequenceSequence& rPoints + , ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos + , const ExplicitIncrementData& rIncrement + , const ExplicitScaleData& rScale + , PolarPlottingPositionHelper* pPosHelper + , double fLogicRadius, double fLogicZ ) +{ + Reference< XScaling > xInverseScaling( NULL ); + if( rScale.Scaling.is() ) + xInverseScaling = rScale.Scaling->getInverseScaling(); + + sal_Int32 nTick = 0; + EquidistantTickIter aIter( rAllTickInfos, rIncrement, 0, 0 ); + for( TickInfo* pTickInfo = aIter.firstInfo() + ; pTickInfo + ; pTickInfo = aIter.nextInfo(), nTick++ ) + { + if(nTick>=rPoints[0].getLength()) + rPoints[0].realloc(rPoints[0].getLength()+30); + + pTickInfo->updateUnscaledValue( xInverseScaling ); + double fLogicAngle = pTickInfo->fUnscaledTickValue; + + drawing::Position3D aScenePosition3D( pPosHelper->transformAngleRadiusToScene( fLogicAngle, fLogicRadius, fLogicZ ) ); + rPoints[0][nTick].X = static_cast<sal_Int32>(aScenePosition3D.PositionX); + rPoints[0][nTick].Y = static_cast<sal_Int32>(aScenePosition3D.PositionY); + } + if(rPoints[0].getLength()>1) + { + rPoints[0].realloc(nTick+1); + rPoints[0][nTick].X = rPoints[0][0].X; + rPoints[0][nTick].Y = rPoints[0][0].Y; + } + else + rPoints[0].realloc(0); +} +#ifdef NOTYET +void VPolarGrid::create2DAngleGrid( const Reference< drawing::XShapes >& xLogicTarget + , ::std::vector< ::std::vector< TickInfo > >& /* rRadiusTickInfos */ + , ::std::vector< ::std::vector< TickInfo > >& rAngleTickInfos + , const ::std::vector<VLineProperties>& rLinePropertiesList ) +{ + Reference< drawing::XShapes > xMainTarget( + this->createGroupShape( xLogicTarget, m_aCID ) ); + + const ExplicitScaleData& rAngleScale = m_pPosHelper->getScales()[0]; + Reference< XScaling > xInverseScaling( NULL ); + if( rAngleScale.Scaling.is() ) + xInverseScaling = rAngleScale.Scaling->getInverseScaling(); + + double fLogicInnerRadius = m_pPosHelper->getInnerLogicRadius(); + double fLogicOuterRadius = m_pPosHelper->getOuterLogicRadius(); + double fLogicZ = -0.5;//as defined + + sal_Int32 nLinePropertiesCount = rLinePropertiesList.size(); + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = rAngleTickInfos.begin(); + sal_Int32 nDepth=0; + /* + //no subgrids so far for polar angle grid (need different radii) + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = rAngleTickInfos.end(); + for( ; aDepthIter != aDepthEnd && nDepth < nLinePropertiesCount + ; aDepthIter++, nDepth++ ) + */ + if(nLinePropertiesCount) + { + //create axis main lines + drawing::PointSequenceSequence aAllPoints; + ::std::vector< TickInfo >::iterator aTickIter = (*aDepthIter).begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = (*aDepthIter).end(); + for( ; aTickIter != aTickEnd; aTickIter++ ) + { + TickInfo& rTickInfo = *aTickIter; + if( !rTickInfo.bPaintIt ) + continue; + + rTickInfo.updateUnscaledValue( xInverseScaling ); + double fLogicAngle = rTickInfo.fUnscaledTickValue; + + drawing::PointSequenceSequence aPoints(1); + aPoints[0].realloc(2); + drawing::Position3D aScenePositionStart( m_pPosHelper->transformAngleRadiusToScene( fLogicAngle, fLogicInnerRadius, fLogicZ ) ); + drawing::Position3D aScenePositionEnd( m_pPosHelper->transformAngleRadiusToScene( fLogicAngle, fLogicOuterRadius, fLogicZ ) ); + aPoints[0][0].X = static_cast<sal_Int32>(aScenePositionStart.PositionX); + aPoints[0][0].Y = static_cast<sal_Int32>(aScenePositionStart.PositionY); + aPoints[0][1].X = static_cast<sal_Int32>(aScenePositionEnd.PositionX); + aPoints[0][1].Y = static_cast<sal_Int32>(aScenePositionEnd.PositionY); + appendPointSequence( aAllPoints, aPoints ); + } + + Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( + xMainTarget, aAllPoints, &rLinePropertiesList[nDepth] ); + //because of this name this line will be used for marking + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + } +} +#endif + +void VPolarGrid::create2DRadiusGrid( const Reference< drawing::XShapes >& xLogicTarget + , ::std::vector< ::std::vector< TickInfo > >& rRadiusTickInfos + , ::std::vector< ::std::vector< TickInfo > >& rAngleTickInfos + , const ::std::vector<VLineProperties>& rLinePropertiesList ) +{ + Reference< drawing::XShapes > xMainTarget( + this->createGroupShape( xLogicTarget, m_aCID ) ); + + const ExplicitScaleData& rRadiusScale = m_pPosHelper->getScales()[1]; + const ExplicitScaleData& rAngleScale = m_pPosHelper->getScales()[0]; + const ExplicitIncrementData& rAngleIncrement = m_aIncrements[0]; + Reference< XScaling > xInverseRadiusScaling( NULL ); + if( rRadiusScale.Scaling.is() ) + xInverseRadiusScaling = rRadiusScale.Scaling->getInverseScaling(); + + sal_Int32 nLinePropertiesCount = rLinePropertiesList.size(); + ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter = rRadiusTickInfos.begin(); + const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd = rRadiusTickInfos.end(); + for( sal_Int32 nDepth=0 + ; aDepthIter != aDepthEnd && nDepth < nLinePropertiesCount + ; aDepthIter++, nDepth++ ) + { + if( !rLinePropertiesList[nDepth].isLineVisible() ) + continue; + + Reference< drawing::XShapes > xTarget( xMainTarget ); + if( nDepth > 0 ) + { + xTarget.set( this->createGroupShape( xLogicTarget + , ObjectIdentifier::addChildParticle( m_aCID, ObjectIdentifier::createChildParticleWithIndex( OBJECTTYPE_SUBGRID, nDepth-1 ) ) + ) ); + if(!xTarget.is()) + xTarget.set( xMainTarget ); + } + + //create axis main lines + drawing::PointSequenceSequence aAllPoints; + ::std::vector< TickInfo >::iterator aTickIter = (*aDepthIter).begin(); + const ::std::vector< TickInfo >::const_iterator aTickEnd = (*aDepthIter).end(); + for( ; aTickIter != aTickEnd; aTickIter++ ) + { + TickInfo& rTickInfo = *aTickIter; + if( !rTickInfo.bPaintIt ) + continue; + + rTickInfo.updateUnscaledValue( xInverseRadiusScaling ); + double fLogicRadius = rTickInfo.fUnscaledTickValue; + double fLogicZ = -0.5;//as defined + + drawing::PointSequenceSequence aPoints(1); + VPolarGrid::createLinePointSequence_ForAngleAxis( aPoints, rAngleTickInfos + , rAngleIncrement, rAngleScale, m_pPosHelper, fLogicRadius, fLogicZ ); + if(aPoints[0].getLength()) + appendPointSequence( aAllPoints, aPoints ); + } + + Reference< drawing::XShape > xShape = m_pShapeFactory->createLine2D( + xTarget, aAllPoints, &rLinePropertiesList[nDepth] ); + //because of this name this line will be used for marking + m_pShapeFactory->setShapeName( xShape, C2U("MarkHandles") ); + } +} + +void SAL_CALL VPolarGrid::createShapes() +{ + DBG_ASSERT(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is(),"Axis is not proper initialized"); + if(!(m_pShapeFactory&&m_xLogicTarget.is()&&m_xFinalTarget.is())) + return; + if(!m_aGridPropertiesList.getLength()) + return; + + //----------------------------------------- + //create all scaled tickmark values + ::std::vector< ::std::vector< TickInfo > > aAngleTickInfos; + ::std::vector< ::std::vector< TickInfo > > aRadiusTickInfos; + getAllTickInfos( 0, aAngleTickInfos ); + getAllTickInfos( 1, aRadiusTickInfos ); + + //----------------------------------------- + ::std::vector<VLineProperties> aLinePropertiesList; + VCartesianGrid::fillLinePropertiesFromGridModel( aLinePropertiesList, m_aGridPropertiesList ); + + //----------------------------------------- + //create tick mark line shapes + if(2==m_nDimension) + { + if(m_nDimensionIndex==1) + this->create2DRadiusGrid( m_xLogicTarget, aRadiusTickInfos, aAngleTickInfos, aLinePropertiesList ); + //else //no Angle Grid so far as this equals exactly the y axis positions + // this->create2DAngleGrid( m_xLogicTarget, aRadiusTickInfos, aAngleTickInfos, aLinePropertiesList ); + } +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VPolarGrid.hxx b/chart2/source/view/axes/VPolarGrid.hxx new file mode 100644 index 000000000000..3289d6c88be8 --- /dev/null +++ b/chart2/source/view/axes/VPolarGrid.hxx @@ -0,0 +1,96 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VPOLARGRID_HXX +#define _CHART2_VPOLARGRID_HXX + +#include "VAxisOrGridBase.hxx" +#include "TickmarkHelper.hxx" +#include "VLineProperties.hxx" +#include <com/sun/star/drawing/PointSequenceSequence.hpp> + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ +class PolarPlottingPositionHelper; + +class VPolarGrid : public VAxisOrGridBase +{ +//------------------------------------------------------------------------- +// public methods +//------------------------------------------------------------------------- +public: + VPolarGrid( sal_Int32 nDimensionIndex, sal_Int32 nDimensionCount + , const ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > > & rGridPropertiesList //main grid, subgrid, subsubgrid etc + ); + virtual ~VPolarGrid(); + + virtual void SAL_CALL createShapes(); + + void setIncrements( const ::com::sun::star::uno::Sequence< + ::com::sun::star::chart2::ExplicitIncrementData >& rIncrements ); + + static void createLinePointSequence_ForAngleAxis( + ::com::sun::star::drawing::PointSequenceSequence& rPoints + , ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement + , const ::com::sun::star::chart2::ExplicitScaleData& rScale + , PolarPlottingPositionHelper* pPosHelper + , double fLogicRadius, double fLogicZ ); + +private: //member + ::com::sun::star::uno::Sequence< + ::com::sun::star::uno::Reference< + ::com::sun::star::beans::XPropertySet > > m_aGridPropertiesList;//main grid, subgrid, subsubgrid etc + PolarPlottingPositionHelper* m_pPosHelper; + ::com::sun::star::uno::Sequence< + ::com::sun::star::chart2::ExplicitIncrementData > m_aIncrements; + + void getAllTickInfos( sal_Int32 nDimensionIndex, ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const; + + void create2DRadiusGrid( const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes >& xLogicTarget + , ::std::vector< ::std::vector< TickInfo > >& rRadiusTickInfos + , ::std::vector< ::std::vector< TickInfo > >& rAngleTickInfos + , const ::std::vector<VLineProperties>& rLinePropertiesList ); +#if NOTYET + void create2DAngleGrid( const ::com::sun::star::uno::Reference< ::com::sun::star::drawing::XShapes >& xLogicTarget + , ::std::vector< ::std::vector< TickInfo > >& rRadiusTickInfos + , ::std::vector< ::std::vector< TickInfo > >& rAngleTickInfos + , const ::std::vector<VLineProperties>& rLinePropertiesList ); +#endif +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/VPolarRadiusAxis.cxx b/chart2/source/view/axes/VPolarRadiusAxis.cxx new file mode 100644 index 000000000000..2206d4d8f559 --- /dev/null +++ b/chart2/source/view/axes/VPolarRadiusAxis.cxx @@ -0,0 +1,186 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_chart2.hxx" + +#include "VPolarRadiusAxis.hxx" +#include "VCartesianAxis.hxx" +#include "PlottingPositionHelper.hxx" +#include "CommonConverters.hxx" +#include <rtl/math.hxx> + +//............................................................................. +namespace chart +{ +//............................................................................. +using namespace ::com::sun::star; +using namespace ::com::sun::star::chart2; +using namespace ::rtl::math; + +VPolarRadiusAxis::VPolarRadiusAxis( const AxisProperties& rAxisProperties + , const uno::Reference< util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionCount ) + : VPolarAxis( rAxisProperties, xNumberFormatsSupplier, 1/*nDimensionIndex*/, nDimensionCount ) +{ + m_aAxisProperties.m_fLabelDirectionSign=0.0; + m_aAxisProperties.m_fInnerDirectionSign=0.0; + m_aAxisProperties.m_bIsMainAxis=false; + m_aAxisProperties.m_aLabelAlignment=LABEL_ALIGN_RIGHT; + m_aAxisProperties.init(); + + m_apAxisWithLabels = std::auto_ptr<VCartesianAxis>( new VCartesianAxis( + m_aAxisProperties,xNumberFormatsSupplier,1/*nDimensionIndex*/,nDimensionCount + ,new PolarPlottingPositionHelper() ) ); +} + +VPolarRadiusAxis::~VPolarRadiusAxis() +{ + delete m_pPosHelper; + m_pPosHelper = NULL; +} + +void VPolarRadiusAxis::setTransformationSceneToScreen( const drawing::HomogenMatrix& rMatrix) +{ + VPolarAxis::setTransformationSceneToScreen( rMatrix ); + m_apAxisWithLabels->setTransformationSceneToScreen( rMatrix ); +} + +void SAL_CALL VPolarRadiusAxis::setExplicitScaleAndIncrement( + const ExplicitScaleData& rScale + , const ExplicitIncrementData& rIncrement ) + throw (uno::RuntimeException) +{ + VPolarAxis::setExplicitScaleAndIncrement( rScale, rIncrement ); + m_apAxisWithLabels->setExplicitScaleAndIncrement( rScale, rIncrement ); +} + +void SAL_CALL VPolarRadiusAxis::initPlotter( const uno::Reference< drawing::XShapes >& xLogicTarget + , const uno::Reference< drawing::XShapes >& xFinalTarget + , const uno::Reference< lang::XMultiServiceFactory >& xShapeFactory + , const rtl::OUString& rCID ) + throw (uno::RuntimeException) +{ + VPolarAxis::initPlotter( xLogicTarget, xFinalTarget, xShapeFactory, rCID ); + m_apAxisWithLabels->initPlotter( xLogicTarget, xFinalTarget, xShapeFactory, rCID ); +} + +void SAL_CALL VPolarRadiusAxis::setScales( const uno::Sequence< ExplicitScaleData >& rScales + , sal_Bool bSwapXAndYAxis ) + throw (uno::RuntimeException) +{ + VPolarAxis::setScales( rScales, bSwapXAndYAxis ); + m_apAxisWithLabels->setScales( rScales, bSwapXAndYAxis ); +} + +void SAL_CALL VPolarRadiusAxis::initAxisLabelProperties( const ::com::sun::star::awt::Size& rFontReferenceSize + , const ::com::sun::star::awt::Rectangle& rMaximumSpaceForLabels ) +{ + VPolarAxis::initAxisLabelProperties( rFontReferenceSize, rMaximumSpaceForLabels ); + m_apAxisWithLabels->initAxisLabelProperties( rFontReferenceSize, rMaximumSpaceForLabels ); +} + +sal_Int32 VPolarRadiusAxis::estimateMaximumAutoMainIncrementCount() +{ + return 2; +} + +bool VPolarRadiusAxis::prepareShapeCreation() +{ + //returns true if all is ready for further shape creation and any shapes need to be created + if( !isAnythingToDraw() ) + return false; + + if( m_xGroupShape_Shapes.is() ) + return true; + + return true; +} + +void SAL_CALL VPolarRadiusAxis::createMaximumLabels() +{ + m_apAxisWithLabels->createMaximumLabels(); +} + +void SAL_CALL VPolarRadiusAxis::updatePositions() +{ + m_apAxisWithLabels->updatePositions(); +} + +void SAL_CALL VPolarRadiusAxis::createLabels() +{ + m_apAxisWithLabels->createLabels(); +} + +void SAL_CALL VPolarRadiusAxis::createShapes() +{ + if( !prepareShapeCreation() ) + return; + + const ExplicitScaleData& rAngleScale = m_pPosHelper->getScales()[0]; + const ExplicitIncrementData& rAngleIncrement = m_aIncrements[0]; + + ::std::vector< ::std::vector< TickInfo > > aAngleTickInfos; + TickmarkHelper aAngleTickmarkHelper( rAngleScale, rAngleIncrement ); + aAngleTickmarkHelper.getAllTicks( aAngleTickInfos ); + + uno::Reference< XScaling > xInverseScaling( NULL ); + if( rAngleScale.Scaling.is() ) + xInverseScaling = rAngleScale.Scaling->getInverseScaling(); + + AxisProperties aAxisProperties(m_aAxisProperties); + + sal_Int32 nTick = 0; + EquidistantTickIter aIter( aAngleTickInfos, rAngleIncrement, 0, 0 ); + for( TickInfo* pTickInfo = aIter.firstInfo() + ; pTickInfo; pTickInfo = aIter.nextInfo(), nTick++ ) + { + if( nTick == 0 ) + { + m_apAxisWithLabels->createShapes(); + continue; + } + + pTickInfo->updateUnscaledValue( xInverseScaling ); + aAxisProperties.m_pfMainLinePositionAtOtherAxis = new double( pTickInfo->fUnscaledTickValue ); + aAxisProperties.m_bDisplayLabels=false; + + //------------------- + VCartesianAxis aAxis(aAxisProperties,m_xNumberFormatsSupplier + ,1,2,new PolarPlottingPositionHelper()); + aAxis.setExplicitScaleAndIncrement( m_aScale, m_aIncrement ); + aAxis.initPlotter(m_xLogicTarget,m_xFinalTarget,m_xShapeFactory, m_aCID ); + aAxis.setTransformationSceneToScreen( B3DHomMatrixToHomogenMatrix( m_aMatrixScreenToScene ) ); + aAxis.setScales( m_pPosHelper->getScales(), false ); + aAxis.initAxisLabelProperties(m_aAxisLabelProperties.m_aFontReferenceSize,m_aAxisLabelProperties.m_aMaximumSpaceForLabels); + aAxis.createShapes(); + } +} + +//............................................................................. +} //namespace chart +//............................................................................. diff --git a/chart2/source/view/axes/VPolarRadiusAxis.hxx b/chart2/source/view/axes/VPolarRadiusAxis.hxx new file mode 100644 index 000000000000..b5c5191e4c98 --- /dev/null +++ b/chart2/source/view/axes/VPolarRadiusAxis.hxx @@ -0,0 +1,98 @@ +/************************************************************************* + * + * 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 + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ +#ifndef _CHART2_VPOLARRADIUSAXIS_HXX +#define _CHART2_VPOLARRADIUSAXIS_HXX + +#include "VPolarAxis.hxx" + +#include <memory> + +//............................................................................. +namespace chart +{ +//............................................................................. + +//----------------------------------------------------------------------------- +/** +*/ + +class VCartesianAxis; + +class VPolarRadiusAxis : public VPolarAxis +{ +public: + VPolarRadiusAxis( const AxisProperties& rAxisProperties + , const ::com::sun::star::uno::Reference< ::com::sun::star::util::XNumberFormatsSupplier >& xNumberFormatsSupplier + , sal_Int32 nDimensionCount ); + virtual ~VPolarRadiusAxis(); + + virtual void SAL_CALL initPlotter( + const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes >& xLogicTarget + , const ::com::sun::star::uno::Reference< + ::com::sun::star::drawing::XShapes >& xFinalTarget + , const ::com::sun::star::uno::Reference< + ::com::sun::star::lang::XMultiServiceFactory >& xFactory + , const rtl::OUString& rCID + ) throw (::com::sun::star::uno::RuntimeException ); + + virtual void setTransformationSceneToScreen( const ::com::sun::star::drawing::HomogenMatrix& rMatrix ); + + virtual void SAL_CALL setScales( + const ::com::sun::star::uno::Sequence< + ::com::sun::star::chart2::ExplicitScaleData >& rScales + , sal_Bool bSwapXAndYAxis ) + throw (::com::sun::star::uno::RuntimeException); + + virtual void SAL_CALL setExplicitScaleAndIncrement( + const ::com::sun::star::chart2::ExplicitScaleData& rScale + , const ::com::sun::star::chart2::ExplicitIncrementData& rIncrement ) + throw (::com::sun::star::uno::RuntimeException); + + virtual void SAL_CALL initAxisLabelProperties( + const ::com::sun::star::awt::Size& rFontReferenceSize + , const ::com::sun::star::awt::Rectangle& rMaximumSpaceForLabels ); + + virtual sal_Int32 estimateMaximumAutoMainIncrementCount(); + + virtual void SAL_CALL createMaximumLabels(); + virtual void SAL_CALL createLabels(); + virtual void SAL_CALL updatePositions(); + + virtual void SAL_CALL createShapes(); + +protected: //methods + virtual bool prepareShapeCreation(); + +private: //member + std::auto_ptr<VCartesianAxis> m_apAxisWithLabels; +}; + +//............................................................................. +} //namespace chart +//............................................................................. +#endif diff --git a/chart2/source/view/axes/makefile.mk b/chart2/source/view/axes/makefile.mk new file mode 100644 index 000000000000..96191a3a3a25 --- /dev/null +++ b/chart2/source/view/axes/makefile.mk @@ -0,0 +1,66 @@ +#************************************************************************* +# +# 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 +# <http://www.openoffice.org/license.html> +# for a copy of the LGPLv3 License. +# +#************************************************************************* + +PRJ= ..$/..$/.. +PRJINC= $(PRJ)$/source +PRJNAME= chart2 +TARGET= chvaxes + +ENABLE_EXCEPTIONS= TRUE + +# --- Settings ----------------------------------------------------- + +.INCLUDE: settings.mk +.INCLUDE: $(PRJ)$/chartview.pmk + +#.IF "$(GUI)" == "WNT" +#CFLAGS+=-GR +#.ENDIF + +# --- export library ------------------------------------------------- + +#object files to build and link together to lib $(SLB)$/$(TARGET).lib +SLOFILES = \ + $(SLO)$/VAxisOrGridBase.obj \ + $(SLO)$/VAxisBase.obj \ + $(SLO)$/TickmarkHelper.obj \ + $(SLO)$/MinimumAndMaximumSupplier.obj \ + $(SLO)$/ScaleAutomatism.obj \ + $(SLO)$/VAxisProperties.obj \ + $(SLO)$/VCartesianAxis.obj \ + $(SLO)$/VCartesianGrid.obj \ + $(SLO)$/VCartesianCoordinateSystem.obj \ + $(SLO)$/VPolarAxis.obj \ + $(SLO)$/VPolarAngleAxis.obj \ + $(SLO)$/VPolarRadiusAxis.obj \ + $(SLO)$/VPolarGrid.obj \ + $(SLO)$/VPolarCoordinateSystem.obj \ + $(SLO)$/VCoordinateSystem.obj + +# --- Targets ----------------------------------------------------------------- + +.INCLUDE: target.mk |