update dev300-m58
[ooovba.git] / chart2 / source / view / charttypes / Splines.cxx
blobbc9c0c458f8414ea5ff65dea0927c0bca30d6e32
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: Splines.cxx,v $
10 * $Revision: 1.11.44.1 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_chart2.hxx"
35 #include "Splines.hxx"
36 #include <rtl/math.hxx>
38 #include <vector>
39 #include <algorithm>
40 #include <functional>
42 // header for DBG_ASSERT
43 #include <tools/debug.hxx>
45 //.............................................................................
46 namespace chart
48 //.............................................................................
49 using namespace ::com::sun::star;
51 namespace
54 template< typename T >
55 struct lcl_EqualsFirstDoubleOfPair : ::std::binary_function< ::std::pair< double, T >, ::std::pair< double, T >, bool >
57 inline bool operator() ( const ::std::pair< double, T > & rOne, const ::std::pair< double, T > & rOther )
59 return ( ::rtl::math::approxEqual( rOne.first, rOther.first ) );
63 //-----------------------------------------------------------------------------
65 typedef ::std::pair< double, double > tPointType;
66 typedef ::std::vector< tPointType > tPointVecType;
67 typedef tPointVecType::size_type lcl_tSizeType;
69 class lcl_SplineCalculation
71 public:
72 /** @descr creates an object that calculates cublic splines on construction
74 @param rSortedPoints the points for which splines shall be calculated, they need to be sorted in x values
75 @param fY1FirstDerivation the resulting spline should have the first
76 derivation equal to this value at the x-value of the first point
77 of rSortedPoints. If fY1FirstDerivation is set to infinity, a natural
78 spline is calculated.
79 @param fYnFirstDerivation the resulting spline should have the first
80 derivation equal to this value at the x-value of the last point
81 of rSortedPoints
83 lcl_SplineCalculation( const tPointVecType & rSortedPoints,
84 double fY1FirstDerivation,
85 double fYnFirstDerivation );
87 /** @descr this function corresponds to the function splint in [1].
89 [1] Numerical Recipies in C, 2nd edition
90 William H. Press, et al.,
91 Section 3.3, page 116
93 double GetInterpolatedValue( double x );
95 private:
96 /// a copy of the points given in the CTOR
97 tPointVecType m_aPoints;
99 /// the result of the Calculate() method
100 ::std::vector< double > m_aSecDerivY;
102 double m_fYp1;
103 double m_fYpN;
105 // these values are cached for performance reasons
106 tPointVecType::size_type m_nKLow;
107 tPointVecType::size_type m_nKHigh;
108 double m_fLastInterpolatedValue;
110 /** @descr this function corresponds to the function spline in [1].
112 [1] Numerical Recipies in C, 2nd edition
113 William H. Press, et al.,
114 Section 3.3, page 115
116 void Calculate();
119 //-----------------------------------------------------------------------------
121 lcl_SplineCalculation::lcl_SplineCalculation(
122 const tPointVecType & rSortedPoints,
123 double fY1FirstDerivation,
124 double fYnFirstDerivation )
125 : m_aPoints( rSortedPoints ),
126 m_fYp1( fY1FirstDerivation ),
127 m_fYpN( fYnFirstDerivation ),
128 m_nKLow( 0 ),
129 m_nKHigh( rSortedPoints.size() - 1 )
131 ::rtl::math::setInf( &m_fLastInterpolatedValue, sal_False );
133 // #108301# remove points that have equal x-values
134 m_aPoints.erase( ::std::unique( m_aPoints.begin(), m_aPoints.end(),
135 lcl_EqualsFirstDoubleOfPair< double >() ),
136 m_aPoints.end() );
137 Calculate();
140 void lcl_SplineCalculation::Calculate()
142 if( m_aPoints.size() <= 1 )
143 return;
145 // n is the last valid index to m_aPoints
146 const tPointVecType::size_type n = m_aPoints.size() - 1;
147 ::std::vector< double > u( n );
148 m_aSecDerivY.resize( n + 1, 0.0 );
150 if( ::rtl::math::isInf( m_fYp1 ) )
152 // natural spline
153 m_aSecDerivY[ 0 ] = 0.0;
154 u[ 0 ] = 0.0;
156 else
158 m_aSecDerivY[ 0 ] = -0.5;
159 double xDiff = ( m_aPoints[ 1 ].first - m_aPoints[ 0 ].first );
160 u[ 0 ] = ( 3.0 / xDiff ) *
161 ((( m_aPoints[ 1 ].second - m_aPoints[ 0 ].second ) / xDiff ) - m_fYp1 );
164 for( tPointVecType::size_type i = 1; i < n; ++i )
166 ::std::pair< double, double >
167 p_i = m_aPoints[ i ],
168 p_im1 = m_aPoints[ i - 1 ],
169 p_ip1 = m_aPoints[ i + 1 ];
171 double sig = ( p_i.first - p_im1.first ) /
172 ( p_ip1.first - p_im1.first );
173 double p = sig * m_aSecDerivY[ i - 1 ] + 2.0;
175 m_aSecDerivY[ i ] = ( sig - 1.0 ) / p;
176 u[ i ] =
177 ( ( p_ip1.second - p_i.second ) /
178 ( p_ip1.first - p_i.first ) ) -
179 ( ( p_i.second - p_im1.second ) /
180 ( p_i.first - p_im1.first ) );
181 u[ i ] =
182 ( 6.0 * u[ i ] / ( p_ip1.first - p_im1.first )
183 - sig * u[ i - 1 ] ) / p;
186 // initialize to values for natural splines (used for m_fYpN equal to
187 // infinity)
188 double qn = 0.0;
189 double un = 0.0;
191 if( ! ::rtl::math::isInf( m_fYpN ) )
193 qn = 0.5;
194 double xDiff = ( m_aPoints[ n ].first - m_aPoints[ n - 1 ].first );
195 un = ( 3.0 / xDiff ) *
196 ( m_fYpN - ( m_aPoints[ n ].second - m_aPoints[ n - 1 ].second ) / xDiff );
199 m_aSecDerivY[ n ] = ( un - qn * u[ n - 1 ] ) * ( qn * m_aSecDerivY[ n - 1 ] + 1.0 );
201 // note: the algorithm in [1] iterates from n-1 to 0, but as size_type
202 // may be (usuall is) an unsigned type, we can not write k >= 0, as this
203 // is always true.
204 for( tPointVecType::size_type k = n; k > 0; --k )
206 ( m_aSecDerivY[ k - 1 ] *= m_aSecDerivY[ k ] ) += u[ k - 1 ];
210 double lcl_SplineCalculation::GetInterpolatedValue( double x )
212 DBG_ASSERT( ( m_aPoints[ 0 ].first <= x ) &&
213 ( x <= m_aPoints[ m_aPoints.size() - 1 ].first ),
214 "Trying to extrapolate" );
216 const tPointVecType::size_type n = m_aPoints.size() - 1;
217 if( x < m_fLastInterpolatedValue )
219 m_nKLow = 0;
220 m_nKHigh = n;
222 // calculate m_nKLow and m_nKHigh
223 // first initialization is done in CTOR
224 while( m_nKHigh - m_nKLow > 1 )
226 tPointVecType::size_type k = ( m_nKHigh + m_nKLow ) / 2;
227 if( m_aPoints[ k ].first > x )
228 m_nKHigh = k;
229 else
230 m_nKLow = k;
233 else
235 while( ( m_aPoints[ m_nKHigh ].first < x ) &&
236 ( m_nKHigh <= n ) )
238 ++m_nKHigh;
239 ++m_nKLow;
241 DBG_ASSERT( m_nKHigh <= n, "Out of Bounds" );
243 m_fLastInterpolatedValue = x;
245 double h = m_aPoints[ m_nKHigh ].first - m_aPoints[ m_nKLow ].first;
246 DBG_ASSERT( h != 0, "Bad input to GetInterpolatedValue()" );
248 double a = ( m_aPoints[ m_nKHigh ].first - x ) / h;
249 double b = ( x - m_aPoints[ m_nKLow ].first ) / h;
251 return ( a * m_aPoints[ m_nKLow ].second +
252 b * m_aPoints[ m_nKHigh ].second +
253 (( a*a*a - a ) * m_aSecDerivY[ m_nKLow ] +
254 ( b*b*b - b ) * m_aSecDerivY[ m_nKHigh ] ) *
255 ( h*h ) / 6.0 );
258 //-----------------------------------------------------------------------------
260 //create knot vector for B-spline
261 double* createTVector( sal_Int32 n, sal_Int32 k )
263 double* t = new double [n + k + 1];
264 for (sal_Int32 i=0; i<=n+k; i++ )
266 if(i < k)
267 t[i] = 0;
268 else if(i <= n)
269 t[i] = i-k+1;
270 else
271 t[i] = n-k+2;
273 return t;
276 //calculate left knot vector
277 double TLeft (double x, sal_Int32 i, sal_Int32 k, const double *t )
279 double deltaT = t[i + k - 1] - t[i];
280 return (deltaT == 0.0)
281 ? 0.0
282 : (x - t[i]) / deltaT;
285 //calculate right knot vector
286 double TRight(double x, sal_Int32 i, sal_Int32 k, const double *t )
288 double deltaT = t[i + k] - t[i + 1];
289 return (deltaT == 0.0)
290 ? 0.0
291 : (t[i + k] - x) / deltaT;
294 //calculate weight vector
295 void BVector(double x, sal_Int32 n, sal_Int32 k, double *b, const double *t)
297 sal_Int32 i = 0;
298 for( i=0; i<=n+k; i++ )
299 b[i]=0;
301 sal_Int32 i0 = (sal_Int32)floor(x) + k - 1;
302 b [i0] = 1;
304 for( sal_Int32 j=2; j<=k; j++ )
305 for( i=0; i<=i0; i++ )
306 b[i] = TLeft(x, i, j, t) * b[i] + TRight(x, i, j, t) * b [i + 1];
309 } // anonymous namespace
311 //-----------------------------------------------------------------------------
312 //-----------------------------------------------------------------------------
313 //-----------------------------------------------------------------------------
315 void SplineCalculater::CalculateCubicSplines(
316 const drawing::PolyPolygonShape3D& rInput
317 , drawing::PolyPolygonShape3D& rResult
318 , sal_Int32 nGranularity )
320 DBG_ASSERT( nGranularity > 0, "Granularity is invalid" );
322 rResult.SequenceX.realloc(0);
323 rResult.SequenceY.realloc(0);
324 rResult.SequenceZ.realloc(0);
326 sal_Int32 nOuterCount = rInput.SequenceX.getLength();
327 if( !nOuterCount )
328 return;
330 rResult.SequenceX.realloc(nOuterCount);
331 rResult.SequenceY.realloc(nOuterCount);
332 rResult.SequenceZ.realloc(nOuterCount);
334 for( sal_Int32 nOuter = 0; nOuter < nOuterCount; ++nOuter )
336 if( rInput.SequenceX[nOuter].getLength() <= 1 )
337 continue; //we need at least two points
339 sal_Int32 nMaxIndexPoints = rInput.SequenceX[nOuter].getLength()-1; // is >=1
340 const double* pOldX = rInput.SequenceX[nOuter].getConstArray();
341 const double* pOldY = rInput.SequenceY[nOuter].getConstArray();
342 const double* pOldZ = rInput.SequenceZ[nOuter].getConstArray();
344 // #i13699# The curve gets a parameter and then for each coordinate a
345 // separate spline will be calculated using the parameter as first argument
346 // and the point coordinate as second argument. Therefore the points need
347 // not to be sorted in its x-coordinates. The parameter is sorted by
348 // construction.
350 ::std::vector < double > aParameter(nMaxIndexPoints+1);
351 aParameter[0]=0.0;
352 for( sal_Int32 nIndex=1; nIndex<=nMaxIndexPoints; nIndex++ )
354 // The euclidian distance leads to curve loops for functions having single extreme points
355 //aParameter[nIndex]=aParameter[nIndex-1]+
356 //sqrt( (pOldX[nIndex]-pOldX[nIndex-1])*(pOldX[nIndex]-pOldX[nIndex-1])+
357 //(pOldY[nIndex]-pOldY[nIndex-1])*(pOldY[nIndex]-pOldY[nIndex-1])+
358 //(pOldZ[nIndex]-pOldZ[nIndex-1])*(pOldZ[nIndex]-pOldZ[nIndex-1]));
360 // use increment of 1 instead
361 aParameter[nIndex]=aParameter[nIndex-1]+1;
363 // Split the calculation to X, Y and Z coordinate
364 tPointVecType aInputX;
365 aInputX.resize(nMaxIndexPoints+1);
366 tPointVecType aInputY;
367 aInputY.resize(nMaxIndexPoints+1);
368 tPointVecType aInputZ;
369 aInputZ.resize(nMaxIndexPoints+1);
370 for (sal_Int32 nN=0;nN<=nMaxIndexPoints; nN++ )
372 aInputX[ nN ].first=aParameter[nN];
373 aInputX[ nN ].second=pOldX[ nN ];
374 aInputY[ nN ].first=aParameter[nN];
375 aInputY[ nN ].second=pOldY[ nN ];
376 aInputZ[ nN ].first=aParameter[nN];
377 aInputZ[ nN ].second=pOldZ[ nN ];
380 // generate a spline for each coordinate. It holds the complete
381 // information to calculate each point of the curve
382 double fXDerivation;
383 double fYDerivation;
384 double fZDerivation;
385 if( pOldX[ 0 ] == pOldX[nMaxIndexPoints] &&
386 pOldY[ 0 ] == pOldY[nMaxIndexPoints] &&
387 pOldZ[ 0 ] == pOldZ[nMaxIndexPoints] )
389 // #i101050# avoid a corner in closed lines, which are smoothed by spline
390 // This derivation are special for parameter of kind 0,1,2,3... If you
391 // change generating parameters (see above), then adapt derivations too.)
392 fXDerivation = 0.5 * (pOldX[1]-pOldX[nMaxIndexPoints-1]);
393 fYDerivation = 0.5 * (pOldY[1]-pOldY[nMaxIndexPoints-1]);
394 fZDerivation = 0.5 * (pOldZ[1]-pOldZ[nMaxIndexPoints-1]);
396 else // generate the kind "natural spline"
398 double fInfty;
399 ::rtl::math::setInf( &fInfty, sal_False );
400 fXDerivation = fInfty;
401 fYDerivation = fInfty;
402 fZDerivation = fInfty;
404 lcl_SplineCalculation aSplineX( aInputX, fXDerivation, fXDerivation );
405 lcl_SplineCalculation aSplineY( aInputY, fYDerivation, fYDerivation );
406 lcl_SplineCalculation aSplineZ( aInputZ, fZDerivation, fZDerivation );
408 // fill result polygon with calculated values
409 rResult.SequenceX[nOuter].realloc( nMaxIndexPoints*nGranularity + 1);
410 rResult.SequenceY[nOuter].realloc( nMaxIndexPoints*nGranularity + 1);
411 rResult.SequenceZ[nOuter].realloc( nMaxIndexPoints*nGranularity + 1);
413 double* pNewX = rResult.SequenceX[nOuter].getArray();
414 double* pNewY = rResult.SequenceY[nOuter].getArray();
415 double* pNewZ = rResult.SequenceZ[nOuter].getArray();
417 sal_Int32 nNewPointIndex = 0; // Index in result points
418 // needed for inner loop
419 double fInc; // step for intermediate points
420 sal_Int32 nj; // for loop
421 double fParam; // a intermediate parameter value
423 for( sal_Int32 ni = 0; ni < nMaxIndexPoints; ni++ )
425 // given point is surely a curve point
426 pNewX[nNewPointIndex] = pOldX[ni];
427 pNewY[nNewPointIndex] = pOldY[ni];
428 pNewZ[nNewPointIndex] = pOldZ[ni];
429 nNewPointIndex++;
431 // calculate intermediate points
432 fInc = ( aParameter[ ni+1 ] - aParameter[ni] ) / static_cast< double >( nGranularity );
433 for(nj = 1; nj < nGranularity; nj++)
435 fParam = aParameter[ni] + ( fInc * static_cast< double >( nj ) );
437 pNewX[nNewPointIndex]=aSplineX.GetInterpolatedValue( fParam );
438 pNewY[nNewPointIndex]=aSplineY.GetInterpolatedValue( fParam );
439 pNewZ[nNewPointIndex]=aSplineZ.GetInterpolatedValue( fParam );
440 nNewPointIndex++;
443 // add last point
444 pNewX[nNewPointIndex] = pOldX[nMaxIndexPoints];
445 pNewY[nNewPointIndex] = pOldY[nMaxIndexPoints];
446 pNewZ[nNewPointIndex] = pOldZ[nMaxIndexPoints];
450 void SplineCalculater::CalculateBSplines(
451 const ::com::sun::star::drawing::PolyPolygonShape3D& rInput
452 , ::com::sun::star::drawing::PolyPolygonShape3D& rResult
453 , sal_Int32 nGranularity
454 , sal_Int32 nDegree )
456 // #issue 72216#
457 // k is the order of the BSpline, nDegree is the degree of its polynoms
458 sal_Int32 k = nDegree + 1;
460 rResult.SequenceX.realloc(0);
461 rResult.SequenceY.realloc(0);
462 rResult.SequenceZ.realloc(0);
464 sal_Int32 nOuterCount = rInput.SequenceX.getLength();
465 if( !nOuterCount )
466 return; // no input
468 rResult.SequenceX.realloc(nOuterCount);
469 rResult.SequenceY.realloc(nOuterCount);
470 rResult.SequenceZ.realloc(nOuterCount);
472 for( sal_Int32 nOuter = 0; nOuter < nOuterCount; ++nOuter )
474 if( rInput.SequenceX[nOuter].getLength() <= 1 )
475 continue; // need at least 2 control points
477 sal_Int32 n = rInput.SequenceX[nOuter].getLength()-1; // maximum index of control points
479 double fCurveparam =0.0; // parameter for the curve
480 // 0<= fCurveparam < fMaxCurveparam
481 double fMaxCurveparam = 2.0+ n - k;
482 if (fMaxCurveparam <= 0.0)
483 return; // not enough control points for desired spline order
485 if (nGranularity < 1)
486 return; //need at least 1 line for each part beween the control points
488 const double* pOldX = rInput.SequenceX[nOuter].getConstArray();
489 const double* pOldY = rInput.SequenceY[nOuter].getConstArray();
490 const double* pOldZ = rInput.SequenceZ[nOuter].getConstArray();
492 // keep this amount of steps to go well with old version
493 sal_Int32 nNewSectorCount = nGranularity * n;
494 double fCurveStep = fMaxCurveparam/static_cast< double >(nNewSectorCount);
496 double *b = new double [n + k + 1]; // values of blending functions
498 const double* t = createTVector(n, k); // knot vector
500 rResult.SequenceX[nOuter].realloc(nNewSectorCount+1);
501 rResult.SequenceY[nOuter].realloc(nNewSectorCount+1);
502 rResult.SequenceZ[nOuter].realloc(nNewSectorCount+1);
503 double* pNewX = rResult.SequenceX[nOuter].getArray();
504 double* pNewY = rResult.SequenceY[nOuter].getArray();
505 double* pNewZ = rResult.SequenceZ[nOuter].getArray();
507 // variables needed inside loop, when calculating one point of output
508 sal_Int32 nPointIndex =0; //index of given contol points
509 double fX=0.0;
510 double fY=0.0;
511 double fZ=0.0; //coordinates of a new BSpline point
513 for(sal_Int32 nNewSector=0; nNewSector<nNewSectorCount; nNewSector++)
514 { // in first looping fCurveparam has value 0.0
516 // Calculate the values of the blending functions for actual curve parameter
517 BVector(fCurveparam, n, k, b, t);
519 // output point(fCurveparam) = sum over {input point * value of blending function}
520 fX = 0.0;
521 fY = 0.0;
522 fZ = 0.0;
523 for (nPointIndex=0;nPointIndex<=n;nPointIndex++)
525 fX +=pOldX[nPointIndex]*b[nPointIndex];
526 fY +=pOldY[nPointIndex]*b[nPointIndex];
527 fZ +=pOldZ[nPointIndex]*b[nPointIndex];
529 pNewX[nNewSector] = fX;
530 pNewY[nNewSector] = fY;
531 pNewZ[nNewSector] = fZ;
533 fCurveparam += fCurveStep; //for next looping
535 // add last control point to BSpline curve
536 pNewX[nNewSectorCount] = pOldX[n];
537 pNewY[nNewSectorCount] = pOldY[n];
538 pNewZ[nNewSectorCount] = pOldZ[n];
540 delete[] t;
541 delete[] b;
545 //.............................................................................
546 } //namespace chart
547 //.............................................................................