1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_chart2.hxx"
30 #include "Tickmarks.hxx"
31 #include "Tickmarks_Equidistant.hxx"
32 #include "Tickmarks_Dates.hxx"
33 #include "ViewDefines.hxx"
34 #include <rtl/math.hxx>
35 #include <tools/debug.hxx>
36 #include <memory>
37 
38 //.............................................................................
39 namespace chart
40 {
41 //.............................................................................
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::chart2;
44 using namespace ::rtl::math;
45 using ::basegfx::B2DVector;
46 
47 TickInfo::TickInfo( const ::com::sun::star::uno::Reference<
48                     ::com::sun::star::chart2::XScaling >& xInverse )
49 : fScaledTickValue( 0.0 )
50 , xInverseScaling( xInverse )
51 , aTickScreenPosition(0.0,0.0)
52 , bPaintIt( true )
53 , xTextShape( NULL )
54 , nFactorForLimitedTextWidth(1)
55 {
56 }
57 
58 double TickInfo::getUnscaledTickValue() const
59 {
60     if( xInverseScaling.is() )
61         return xInverseScaling->doScaling( fScaledTickValue );
62     else
63         return fScaledTickValue;
64 }
65 
66 sal_Int32 TickInfo::getScreenDistanceBetweenTicks( const TickInfo& rOherTickInfo ) const
67 {
68     //return the positive distance between the two first tickmarks in screen values
69 
70     B2DVector aDistance = rOherTickInfo.aTickScreenPosition - aTickScreenPosition;
71     sal_Int32 nRet = static_cast<sal_Int32>(aDistance.getLength());
72     if(nRet<0)
73         nRet *= -1;
74     return nRet;
75 }
76 
77 PureTickIter::PureTickIter( ::std::vector< TickInfo >& rTickInfoVector )
78             : m_rTickVector(rTickInfoVector)
79             , m_aTickIter(m_rTickVector.begin())
80 {
81 }
82 PureTickIter::~PureTickIter()
83 {
84 }
85 TickInfo* PureTickIter::firstInfo()
86 {
87     m_aTickIter = m_rTickVector.begin();
88     if(m_aTickIter!=m_rTickVector.end())
89         return &*m_aTickIter;
90     return 0;
91 }
92 TickInfo* PureTickIter::nextInfo()
93 {
94     if(m_aTickIter!=m_rTickVector.end())
95     {
96         m_aTickIter++;
97         if(m_aTickIter!=m_rTickVector.end())
98             return &*m_aTickIter;
99     }
100     return 0;
101 }
102 
103 //-----------------------------------------------------------------------------
104 //-----------------------------------------------------------------------------
105 //-----------------------------------------------------------------------------
106 
107 TickFactory::TickFactory(
108           const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement )
109             : m_rScale( rScale )
110             , m_rIncrement( rIncrement )
111             , m_xInverseScaling(NULL)
112 {
113     //@todo: make sure that the scale is valid for the scaling
114 
115     if( m_rScale.Scaling.is() )
116     {
117         m_xInverseScaling = m_rScale.Scaling->getInverseScaling();
118         DBG_ASSERT( m_xInverseScaling.is(), "each Scaling needs to return a inverse Scaling" );
119     }
120 
121     m_fScaledVisibleMin = m_rScale.Minimum;
122     if( m_xInverseScaling.is() )
123         m_fScaledVisibleMin = m_rScale.Scaling->doScaling(m_fScaledVisibleMin);
124 
125     m_fScaledVisibleMax = m_rScale.Maximum;
126     if( m_xInverseScaling.is() )
127         m_fScaledVisibleMax = m_rScale.Scaling->doScaling(m_fScaledVisibleMax);
128 }
129 
130 TickFactory::~TickFactory()
131 {
132 }
133 
134 bool TickFactory::isDateAxis() const
135 {
136     return m_rScale.AxisType == AxisType::DATE;
137 }
138 
139 void TickFactory::getAllTicks( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
140 {
141     if( isDateAxis() )
142         DateTickFactory( m_rScale, m_rIncrement ).getAllTicks( rAllTickInfos );
143     else
144         EquidistantTickFactory( m_rScale, m_rIncrement ).getAllTicks( rAllTickInfos );
145 }
146 
147 void TickFactory::getAllTicksShifted( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
148 {
149     if( isDateAxis() )
150         DateTickFactory( m_rScale, m_rIncrement ).getAllTicksShifted( rAllTickInfos );
151     else
152         EquidistantTickFactory( m_rScale, m_rIncrement ).getAllTicksShifted( rAllTickInfos );
153 }
154 
155 //-----------------------------------------------------------------------------
156 // ___TickFactory_2D___
157 //-----------------------------------------------------------------------------
158 TickFactory_2D::TickFactory_2D(
159           const ExplicitScaleData& rScale, const ExplicitIncrementData& rIncrement
160           //, double fStrech_SceneToScreen, double fOffset_SceneToScreen )
161           , const B2DVector& rStartScreenPos, const B2DVector& rEndScreenPos
162           , const B2DVector& rAxisLineToLabelLineShift )
163           : TickFactory( rScale, rIncrement )
164           , m_aAxisStartScreenPosition2D(rStartScreenPos)
165           , m_aAxisEndScreenPosition2D(rEndScreenPos)
166           , m_aAxisLineToLabelLineShift(rAxisLineToLabelLineShift)
167           , m_fStrech_LogicToScreen(1.0)
168           , m_fOffset_LogicToScreen(0.0)
169 {
170     double fWidthY = m_fScaledVisibleMax - m_fScaledVisibleMin;
171     if( AxisOrientation_MATHEMATICAL==m_rScale.Orientation )
172     {
173         m_fStrech_LogicToScreen = 1.0/fWidthY;
174         m_fOffset_LogicToScreen = -m_fScaledVisibleMin;
175     }
176     else
177     {
178         B2DVector aSwap(m_aAxisStartScreenPosition2D);
179         m_aAxisStartScreenPosition2D = m_aAxisEndScreenPosition2D;
180         m_aAxisEndScreenPosition2D = aSwap;
181 
182         m_fStrech_LogicToScreen = -1.0/fWidthY;
183         m_fOffset_LogicToScreen = -m_fScaledVisibleMax;
184     }
185 }
186 
187 TickFactory_2D::~TickFactory_2D()
188 {
189 }
190 
191 bool TickFactory_2D::isHorizontalAxis() const
192 {
193     return ( m_aAxisStartScreenPosition2D.getY() == m_aAxisEndScreenPosition2D.getY() );
194 }
195 bool TickFactory_2D::isVerticalAxis() const
196 {
197     return ( m_aAxisStartScreenPosition2D.getX() == m_aAxisEndScreenPosition2D.getX() );
198 }
199 
200 //static
201 sal_Int32 TickFactory_2D::getTickScreenDistance( TickIter& rIter )
202 {
203     //return the positive distance between the two first tickmarks in screen values
204     //if there are less than two tickmarks -1 is returned
205 
206     const TickInfo* pFirstTickInfo = rIter.firstInfo();
207     const TickInfo* pSecondTickInfo = rIter.nextInfo();
208     if(!pSecondTickInfo  || !pFirstTickInfo)
209         return -1;
210 
211     return pFirstTickInfo->getScreenDistanceBetweenTicks( *pSecondTickInfo );
212 }
213 
214 B2DVector TickFactory_2D::getTickScreenPosition2D( double fScaledLogicTickValue ) const
215 {
216     B2DVector aRet(m_aAxisStartScreenPosition2D);
217     aRet += (m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D)
218                 *((fScaledLogicTickValue+m_fOffset_LogicToScreen)*m_fStrech_LogicToScreen);
219     return aRet;
220 }
221 
222 void TickFactory_2D::addPointSequenceForTickLine( drawing::PointSequenceSequence& rPoints
223                                 , sal_Int32 nSequenceIndex
224                                 , double fScaledLogicTickValue, double fInnerDirectionSign
225                                 , const TickmarkProperties& rTickmarkProperties
226                                 , bool bPlaceAtLabels ) const
227 {
228     if( fInnerDirectionSign==0.0 )
229         fInnerDirectionSign = 1.0;
230 
231     B2DVector aTickScreenPosition = this->getTickScreenPosition2D(fScaledLogicTickValue);
232     if( bPlaceAtLabels )
233         aTickScreenPosition += m_aAxisLineToLabelLineShift;
234 
235     B2DVector aMainDirection = m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D;
236     aMainDirection.normalize();
237     B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
238     aOrthoDirection *= fInnerDirectionSign;
239     aOrthoDirection.normalize();
240 
241     B2DVector aStart = aTickScreenPosition + aOrthoDirection*rTickmarkProperties.RelativePos;
242     B2DVector aEnd = aStart - aOrthoDirection*rTickmarkProperties.Length;
243 
244     rPoints[nSequenceIndex].realloc(2);
245     rPoints[nSequenceIndex][0].X = static_cast<sal_Int32>(aStart.getX());
246     rPoints[nSequenceIndex][0].Y = static_cast<sal_Int32>(aStart.getY());
247     rPoints[nSequenceIndex][1].X = static_cast<sal_Int32>(aEnd.getX());
248     rPoints[nSequenceIndex][1].Y = static_cast<sal_Int32>(aEnd.getY());
249 }
250 
251 B2DVector TickFactory_2D::getDistanceAxisTickToText( const AxisProperties& rAxisProperties, bool bIncludeFarAwayDistanceIfSo, bool bIncludeSpaceBetweenTickAndText ) const
252 {
253     bool bFarAwayLabels = false;
254     if( ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_START == rAxisProperties.m_eLabelPos
255         || ::com::sun::star::chart::ChartAxisLabelPosition_OUTSIDE_END == rAxisProperties.m_eLabelPos )
256         bFarAwayLabels = true;
257 
258     double fInnerDirectionSign = rAxisProperties.m_fInnerDirectionSign;
259     if( fInnerDirectionSign==0.0 )
260         fInnerDirectionSign = 1.0;
261 
262     B2DVector aMainDirection = m_aAxisEndScreenPosition2D-m_aAxisStartScreenPosition2D;
263     aMainDirection.normalize();
264     B2DVector aOrthoDirection(-aMainDirection.getY(),aMainDirection.getX());
265     aOrthoDirection *= fInnerDirectionSign;
266     aOrthoDirection.normalize();
267 
268     B2DVector aStart(0,0), aEnd(0,0);
269     if( bFarAwayLabels )
270     {
271         TickmarkProperties aProps( AxisProperties::getBiggestTickmarkProperties() );
272         aStart = aOrthoDirection*aProps.RelativePos;
273         aEnd = aStart - aOrthoDirection*aProps.Length;
274     }
275     else
276     {
277         for( sal_Int32 nN=rAxisProperties.m_aTickmarkPropertiesList.size();nN--;)
278         {
279             const TickmarkProperties& rProps = rAxisProperties.m_aTickmarkPropertiesList[nN];
280             B2DVector aNewStart = aOrthoDirection*rProps.RelativePos;
281             B2DVector aNewEnd = aNewStart - aOrthoDirection*rProps.Length;
282             if(aNewStart.getLength()>aStart.getLength())
283                 aStart=aNewStart;
284             if(aNewEnd.getLength()>aEnd.getLength())
285                 aEnd=aNewEnd;
286         }
287     }
288 
289     B2DVector aLabelDirection(aStart);
290     if( rAxisProperties.m_fInnerDirectionSign != rAxisProperties.m_fLabelDirectionSign )
291         aLabelDirection = aEnd;
292 
293     B2DVector aOrthoLabelDirection(aOrthoDirection);
294     if( rAxisProperties.m_fInnerDirectionSign != rAxisProperties.m_fLabelDirectionSign )
295         aOrthoLabelDirection*=-1.0;
296     aOrthoLabelDirection.normalize();
297     if( bIncludeSpaceBetweenTickAndText )
298         aLabelDirection += aOrthoLabelDirection*AXIS2D_TICKLABELSPACING;
299     if( bFarAwayLabels && bIncludeFarAwayDistanceIfSo )
300         aLabelDirection += m_aAxisLineToLabelLineShift;
301     return aLabelDirection;
302 }
303 
304 void TickFactory_2D::createPointSequenceForAxisMainLine( drawing::PointSequenceSequence& rPoints ) const
305 {
306     rPoints[0].realloc(2);
307     rPoints[0][0].X = static_cast<sal_Int32>(m_aAxisStartScreenPosition2D.getX());
308     rPoints[0][0].Y = static_cast<sal_Int32>(m_aAxisStartScreenPosition2D.getY());
309     rPoints[0][1].X = static_cast<sal_Int32>(m_aAxisEndScreenPosition2D.getX());
310     rPoints[0][1].Y = static_cast<sal_Int32>(m_aAxisEndScreenPosition2D.getY());
311 }
312 
313 void TickFactory_2D::updateScreenValues( ::std::vector< ::std::vector< TickInfo > >& rAllTickInfos ) const
314 {
315     //get the transformed screen values for all tickmarks in rAllTickInfos
316     ::std::vector< ::std::vector< TickInfo > >::iterator aDepthIter       = rAllTickInfos.begin();
317     const ::std::vector< ::std::vector< TickInfo > >::const_iterator aDepthEnd  = rAllTickInfos.end();
318     for( ; aDepthIter != aDepthEnd; aDepthIter++ )
319     {
320         ::std::vector< TickInfo >::iterator       aTickIter = (*aDepthIter).begin();
321         const ::std::vector< TickInfo >::const_iterator aTickEnd  = (*aDepthIter).end();
322         for( ; aTickIter != aTickEnd; aTickIter++ )
323         {
324             TickInfo& rTickInfo = (*aTickIter);
325             rTickInfo.aTickScreenPosition =
326                 this->getTickScreenPosition2D( rTickInfo.fScaledTickValue );
327         }
328     }
329 }
330 
331 //.............................................................................
332 } //namespace chart
333 //.............................................................................
334