1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "Splines.hxx"
21 #include <rtl/math.hxx>
22 #include <osl/diagnose.h>
27 #include <boost/scoped_array.hpp>
29 #define MAX_BSPLINE_DEGREE 15
33 using namespace ::com::sun::star
;
38 typedef ::std::pair
< double, double > tPointType
;
39 typedef ::std::vector
< tPointType
> tPointVecType
;
40 typedef tPointVecType::size_type lcl_tSizeType
;
42 class lcl_SplineCalculation
45 /** @descr creates an object that calculates cublic splines on construction
47 @param rSortedPoints the points for which splines shall be calculated, they need to be sorted in x values
48 @param fY1FirstDerivation the resulting spline should have the first
49 derivation equal to this value at the x-value of the first point
50 of rSortedPoints. If fY1FirstDerivation is set to infinity, a natural
52 @param fYnFirstDerivation the resulting spline should have the first
53 derivation equal to this value at the x-value of the last point
56 lcl_SplineCalculation( const tPointVecType
& rSortedPoints
,
57 double fY1FirstDerivation
,
58 double fYnFirstDerivation
);
60 /** @descr creates an object that calculates cublic splines on construction
61 for the special case of periodic cubic spline
63 @param rSortedPoints the points for which splines shall be calculated,
64 they need to be sorted in x values. First and last y value must be equal
66 lcl_SplineCalculation( const tPointVecType
& rSortedPoints
);
68 /** @descr this function corresponds to the function splint in [1].
70 [1] Numerical Recipies in C, 2nd edition
71 William H. Press, et al.,
74 double GetInterpolatedValue( double x
);
77 /// a copy of the points given in the CTOR
78 tPointVecType m_aPoints
;
80 /// the result of the Calculate() method
81 ::std::vector
< double > m_aSecDerivY
;
86 // these values are cached for performance reasons
87 lcl_tSizeType m_nKLow
;
88 lcl_tSizeType m_nKHigh
;
89 double m_fLastInterpolatedValue
;
91 /** @descr this function corresponds to the function spline in [1].
93 [1] Numerical Recipies in C, 2nd edition
94 William H. Press, et al.,
99 /** @descr this function corresponds to the algorithm 4.76 in [2] and
102 [2] Engeln-Müllges, Gisela: Numerik-Algorithmen: Verfahren, Beispiele, Anwendungen
103 Springer, Berlin; Auflage: 9., überarb. und erw. A. (8. Dezember 2004)
104 Section 4.10.2, page 175
106 [3] Hanrath, Wilhelm: Mathematik III / Numerik, Vorlesungsskript zur
107 Veranstaltung im WS 2007/2008
108 Fachhochschule Aachen, 2009-09-19
109 Numerik_01.pdf, downloaded 2011-04-19 via
110 http://www.fh-aachen.de/index.php?id=11424&no_cache=1&file=5016&uid=44191
111 Section 5.3, page 129
113 void CalculatePeriodic();
116 lcl_SplineCalculation::lcl_SplineCalculation(
117 const tPointVecType
& rSortedPoints
,
118 double fY1FirstDerivation
,
119 double fYnFirstDerivation
)
120 : m_aPoints( rSortedPoints
),
121 m_fYp1( fY1FirstDerivation
),
122 m_fYpN( fYnFirstDerivation
),
124 m_nKHigh( rSortedPoints
.size() - 1 ),
125 m_fLastInterpolatedValue(0.0)
127 ::rtl::math::setInf( &m_fLastInterpolatedValue
, false );
131 lcl_SplineCalculation::lcl_SplineCalculation(
132 const tPointVecType
& rSortedPoints
)
133 : m_aPoints( rSortedPoints
),
134 m_fYp1( 0.0 ), /*dummy*/
135 m_fYpN( 0.0 ), /*dummy*/
137 m_nKHigh( rSortedPoints
.size() - 1 ),
138 m_fLastInterpolatedValue(0.0)
140 ::rtl::math::setInf( &m_fLastInterpolatedValue
, false );
144 void lcl_SplineCalculation::Calculate()
146 if( m_aPoints
.size() <= 1 )
149 // n is the last valid index to m_aPoints
150 const lcl_tSizeType n
= m_aPoints
.size() - 1;
151 ::std::vector
< double > u( n
);
152 m_aSecDerivY
.resize( n
+ 1, 0.0 );
154 if( ::rtl::math::isInf( m_fYp1
) )
157 m_aSecDerivY
[ 0 ] = 0.0;
162 m_aSecDerivY
[ 0 ] = -0.5;
163 double xDiff
= ( m_aPoints
[ 1 ].first
- m_aPoints
[ 0 ].first
);
164 u
[ 0 ] = ( 3.0 / xDiff
) *
165 ((( m_aPoints
[ 1 ].second
- m_aPoints
[ 0 ].second
) / xDiff
) - m_fYp1
);
168 for( lcl_tSizeType i
= 1; i
< n
; ++i
)
171 p_i
= m_aPoints
[ i
],
172 p_im1
= m_aPoints
[ i
- 1 ],
173 p_ip1
= m_aPoints
[ i
+ 1 ];
175 double sig
= ( p_i
.first
- p_im1
.first
) /
176 ( p_ip1
.first
- p_im1
.first
);
177 double p
= sig
* m_aSecDerivY
[ i
- 1 ] + 2.0;
179 m_aSecDerivY
[ i
] = ( sig
- 1.0 ) / p
;
181 ( ( p_ip1
.second
- p_i
.second
) /
182 ( p_ip1
.first
- p_i
.first
) ) -
183 ( ( p_i
.second
- p_im1
.second
) /
184 ( p_i
.first
- p_im1
.first
) );
186 ( 6.0 * u
[ i
] / ( p_ip1
.first
- p_im1
.first
)
187 - sig
* u
[ i
- 1 ] ) / p
;
190 // initialize to values for natural splines (used for m_fYpN equal to
195 if( ! ::rtl::math::isInf( m_fYpN
) )
198 double xDiff
= ( m_aPoints
[ n
].first
- m_aPoints
[ n
- 1 ].first
);
199 un
= ( 3.0 / xDiff
) *
200 ( m_fYpN
- ( m_aPoints
[ n
].second
- m_aPoints
[ n
- 1 ].second
) / xDiff
);
203 m_aSecDerivY
[ n
] = ( un
- qn
* u
[ n
- 1 ] ) * ( qn
* m_aSecDerivY
[ n
- 1 ] + 1.0 );
205 // note: the algorithm in [1] iterates from n-1 to 0, but as size_type
206 // may be (usuall is) an unsigned type, we can not write k >= 0, as this
208 for( lcl_tSizeType k
= n
; k
> 0; --k
)
210 ( m_aSecDerivY
[ k
- 1 ] *= m_aSecDerivY
[ k
] ) += u
[ k
- 1 ];
214 void lcl_SplineCalculation::CalculatePeriodic()
216 if( m_aPoints
.size() <= 1 )
219 // n is the last valid index to m_aPoints
220 const lcl_tSizeType n
= m_aPoints
.size() - 1;
222 // u is used for vector f in A*c=f in [3], vector a in Ax=a in [2],
223 // vector z in Rtranspose z = a and Dr=z in [2]
224 ::std::vector
< double > u( n
+ 1, 0.0 );
226 // used for vector c in A*c=f and vector x in Ax=a in [2]
227 m_aSecDerivY
.resize( n
+ 1, 0.0 );
229 // diagonal of matrix A, used index 1 to n
230 ::std::vector
< double > Adiag( n
+ 1, 0.0 );
232 // secondary diagonal of matrix A with index 1 to n-1 and upper right element in A[n]
233 ::std::vector
< double > Aupper( n
+ 1, 0.0 );
235 // diagonal of matrix D in A=(R transpose)*D*R in [2], used index 1 to n
236 ::std::vector
< double > Ddiag( n
+1, 0.0 );
238 // right column of matrix R, used index 1 to n-2
239 ::std::vector
< double > Rright( n
-1, 0.0 );
241 // secondary diagonal of matrix R, used index 1 to n-1
242 ::std::vector
< double > Rupper( n
, 0.0 );
247 { // special handling of three polynomials, that are four points
248 double xDiff0
= m_aPoints
[ 1 ].first
- m_aPoints
[ 0 ].first
;
249 double xDiff1
= m_aPoints
[ 2 ].first
- m_aPoints
[ 1 ].first
;
250 double xDiff2
= m_aPoints
[ 3 ].first
- m_aPoints
[ 2 ].first
;
251 double xDiff2p1
= xDiff2
+ xDiff1
;
252 double xDiff0p2
= xDiff0
+ xDiff2
;
253 double xDiff1p0
= xDiff1
+ xDiff0
;
254 double fFactor
= 1.5 / (xDiff0
*xDiff1
+ xDiff1
*xDiff2
+ xDiff2
*xDiff0
);
255 double yDiff0
= (m_aPoints
[ 1 ].second
- m_aPoints
[ 0 ].second
) / xDiff0
;
256 double yDiff1
= (m_aPoints
[ 2 ].second
- m_aPoints
[ 1 ].second
) / xDiff1
;
257 double yDiff2
= (m_aPoints
[ 0 ].second
- m_aPoints
[ 2 ].second
) / xDiff2
;
258 m_aSecDerivY
[ 1 ] = fFactor
* (yDiff1
*xDiff2p1
- yDiff0
*xDiff0p2
);
259 m_aSecDerivY
[ 2 ] = fFactor
* (yDiff2
*xDiff0p2
- yDiff1
*xDiff1p0
);
260 m_aSecDerivY
[ 3 ] = fFactor
* (yDiff0
*xDiff1p0
- yDiff2
*xDiff2p1
);
261 m_aSecDerivY
[ 0 ] = m_aSecDerivY
[ 3 ];
265 // special handling of two polynomials, that are three points
266 double xDiff0
= m_aPoints
[ 1 ].first
- m_aPoints
[ 0 ].first
;
267 double xDiff1
= m_aPoints
[ 2 ].first
- m_aPoints
[ 1 ].first
;
268 double fHelp
= 3.0 * (m_aPoints
[ 0 ].second
- m_aPoints
[ 1 ].second
) / (xDiff0
*xDiff1
);
269 m_aSecDerivY
[ 1 ] = fHelp
;
270 m_aSecDerivY
[ 2 ] = -fHelp
;
271 m_aSecDerivY
[ 0 ] = m_aSecDerivY
[ 2 ] ;
275 // should be handled with natural spline, periodic not possible.
280 double xDiff_i
=1.0; // values are dummy;
281 double xDiff_im1
=1.0;
282 double yDiff_i
= 1.0;
283 double yDiff_im1
= 1.0;
284 // fill matrix A and fill right side vector u
285 for( lcl_tSizeType i
=1; i
<n
; ++i
)
287 xDiff_im1
= m_aPoints
[ i
].first
- m_aPoints
[ i
-1 ].first
;
288 xDiff_i
= m_aPoints
[ i
+1 ].first
- m_aPoints
[ i
].first
;
289 yDiff_im1
= (m_aPoints
[ i
].second
- m_aPoints
[ i
-1 ].second
) / xDiff_im1
;
290 yDiff_i
= (m_aPoints
[ i
+1 ].second
- m_aPoints
[ i
].second
) / xDiff_i
;
291 Adiag
[ i
] = 2 * (xDiff_im1
+ xDiff_i
);
292 Aupper
[ i
] = xDiff_i
;
293 u
[ i
] = 3 * (yDiff_i
- yDiff_im1
);
295 xDiff_im1
= m_aPoints
[ n
].first
- m_aPoints
[ n
-1 ].first
;
296 xDiff_i
= m_aPoints
[ 1 ].first
- m_aPoints
[ 0 ].first
;
297 yDiff_im1
= (m_aPoints
[ n
].second
- m_aPoints
[ n
-1 ].second
) / xDiff_im1
;
298 yDiff_i
= (m_aPoints
[ 1 ].second
- m_aPoints
[ 0 ].second
) / xDiff_i
;
299 Adiag
[ n
] = 2 * (xDiff_im1
+ xDiff_i
);
300 Aupper
[ n
] = xDiff_i
;
301 u
[ n
] = 3 * (yDiff_i
- yDiff_im1
);
303 // decomposite A=(R transpose)*D*R
305 Rupper
[1] = Aupper
[1] / Ddiag
[1];
306 Rright
[1] = Aupper
[n
] / Ddiag
[1];
307 for( lcl_tSizeType i
=2; i
<=n
-2; ++i
)
309 Ddiag
[i
] = Adiag
[i
] - Aupper
[ i
-1 ] * Rupper
[ i
-1 ];
310 Rupper
[ i
] = Aupper
[ i
] / Ddiag
[ i
];
311 Rright
[ i
] = - Rright
[ i
-1 ] * Aupper
[ i
-1 ] / Ddiag
[ i
];
313 Ddiag
[ n
-1 ] = Adiag
[ n
-1 ] - Aupper
[ n
-2 ] * Rupper
[ n
-2 ];
314 Rupper
[ n
-1 ] = ( Aupper
[ n
-1 ] - Aupper
[ n
-2 ] * Rright
[ n
-2] ) / Ddiag
[ n
-1 ];
316 for ( lcl_tSizeType i
=1; i
<=n
-2; ++i
)
318 fSum
+= Ddiag
[ i
] * Rright
[ i
] * Rright
[ i
];
320 Ddiag
[ n
] = Adiag
[ n
] - fSum
- Ddiag
[ n
-1 ] * Rupper
[ n
-1 ] * Rupper
[ n
-1 ]; // bug in [2]!
322 // solve forward (R transpose)*z=u, overwrite u with z
323 for ( lcl_tSizeType i
=2; i
<=n
-1; ++i
)
325 u
[ i
] -= u
[ i
-1 ]* Rupper
[ i
-1 ];
328 for ( lcl_tSizeType i
=1; i
<=n
-2; ++i
)
330 fSum
+= Rright
[ i
] * u
[ i
];
332 u
[ n
] = u
[ n
] - fSum
- Rupper
[ n
- 1] * u
[ n
-1 ];
334 // solve forward D*r=z, z is in u, overwrite u with r
335 for ( lcl_tSizeType i
=1; i
<=n
; ++i
)
337 u
[ i
] = u
[i
] / Ddiag
[ i
];
340 // solve backward R*x= r, r is in u
341 m_aSecDerivY
[ n
] = u
[ n
];
342 m_aSecDerivY
[ n
-1 ] = u
[ n
-1 ] - Rupper
[ n
-1 ] * m_aSecDerivY
[ n
];
343 for ( lcl_tSizeType i
=n
-2; i
>=1; --i
)
345 m_aSecDerivY
[ i
] = u
[ i
] - Rupper
[ i
] * m_aSecDerivY
[ i
+1 ] - Rright
[ i
] * m_aSecDerivY
[ n
];
348 m_aSecDerivY
[ 0 ] = m_aSecDerivY
[ n
];
351 // adapt m_aSecDerivY for usage in GetInterpolatedValue()
352 for( lcl_tSizeType i
= 0; i
<= n
; ++i
)
354 m_aSecDerivY
[ i
] *= 2.0;
359 double lcl_SplineCalculation::GetInterpolatedValue( double x
)
361 OSL_PRECOND( ( m_aPoints
[ 0 ].first
<= x
) &&
362 ( x
<= m_aPoints
[ m_aPoints
.size() - 1 ].first
),
363 "Trying to extrapolate" );
365 const lcl_tSizeType n
= m_aPoints
.size() - 1;
366 if( x
< m_fLastInterpolatedValue
)
371 // calculate m_nKLow and m_nKHigh
372 // first initialization is done in CTOR
373 while( m_nKHigh
- m_nKLow
> 1 )
375 lcl_tSizeType k
= ( m_nKHigh
+ m_nKLow
) / 2;
376 if( m_aPoints
[ k
].first
> x
)
384 while( ( m_aPoints
[ m_nKHigh
].first
< x
) &&
390 OSL_ENSURE( m_nKHigh
<= n
, "Out of Bounds" );
392 m_fLastInterpolatedValue
= x
;
394 double h
= m_aPoints
[ m_nKHigh
].first
- m_aPoints
[ m_nKLow
].first
;
395 OSL_ENSURE( h
!= 0, "Bad input to GetInterpolatedValue()" );
397 double a
= ( m_aPoints
[ m_nKHigh
].first
- x
) / h
;
398 double b
= ( x
- m_aPoints
[ m_nKLow
].first
) / h
;
400 return ( a
* m_aPoints
[ m_nKLow
].second
+
401 b
* m_aPoints
[ m_nKHigh
].second
+
402 (( a
*a
*a
- a
) * m_aSecDerivY
[ m_nKLow
] +
403 ( b
*b
*b
- b
) * m_aSecDerivY
[ m_nKHigh
] ) *
407 // helper methods for B-spline
409 // Create parameter t_0 to t_n using the centripetal method with a power of 0.5
410 bool createParameterT(const tPointVecType
& rUniquePoints
, double* t
)
411 { // precondition: no adjacent identical points
412 // postcondition: 0 = t_0 < t_1 < ... < t_n = 1
413 bool bIsSuccessful
= true;
414 const lcl_tSizeType n
= rUniquePoints
.size() - 1;
418 double fDiffMax
= 1.0; //dummy values
419 double fDenominator
= 0.0; // initialized for summing up
420 for (lcl_tSizeType i
=1; i
<=n
; ++i
)
421 { // 4th root(dx^2+dy^2)
422 dx
= rUniquePoints
[i
].first
- rUniquePoints
[i
-1].first
;
423 dy
= rUniquePoints
[i
].second
- rUniquePoints
[i
-1].second
;
424 // scaling to avoid underflow or overflow
425 fDiffMax
= (fabs(dx
)>fabs(dy
)) ? fabs(dx
) : fabs(dy
);
428 bIsSuccessful
= false;
435 fDenominator
+= sqrt(sqrt(dx
* dx
+ dy
* dy
)) * sqrt(fDiffMax
);
438 if (fDenominator
== 0.0)
440 bIsSuccessful
= false;
444 for (lcl_tSizeType j
=1; j
<=n
; ++j
)
446 double fNumerator
= 0.0;
447 for (lcl_tSizeType i
=1; i
<=j
; ++i
)
449 dx
= rUniquePoints
[i
].first
- rUniquePoints
[i
-1].first
;
450 dy
= rUniquePoints
[i
].second
- rUniquePoints
[i
-1].second
;
451 fDiffMax
= (fabs(dx
)>fabs(dy
)) ? fabs(dx
) : fabs(dy
);
452 // same as above, so should not be zero
455 fNumerator
+= sqrt(sqrt(dx
* dx
+ dy
* dy
)) * sqrt(fDiffMax
);
457 t
[j
] = fNumerator
/ fDenominator
;
460 // postcondition check
462 double fPrevious
= 0.0;
463 for (lcl_tSizeType i
=1; i
<= n
&& bIsSuccessful
; ++i
)
465 if (fPrevious
>= t
[i
])
467 bIsSuccessful
= false;
475 return bIsSuccessful
;
478 void createKnotVector(const lcl_tSizeType n
, const sal_uInt32 p
, double* t
, double* u
)
479 { // precondition: 0 = t_0 < t_1 < ... < t_n = 1
480 for (lcl_tSizeType j
= 0; j
<= p
; ++j
)
484 for (lcl_tSizeType j
= 1; j
<= n
-p
; ++j
)
487 for (lcl_tSizeType i
= j
; i
<= j
+p
-1; ++i
)
494 for (lcl_tSizeType j
= n
+1; j
<= n
+1+p
; ++j
)
500 void applyNtoParameterT(const lcl_tSizeType i
,const double tk
,const sal_uInt32 p
,const double* u
, double* rowN
)
502 // get N_p(t_k) recursively, only N_(i-p) till N_(i) are relevant, all other N_# are zero
504 // initialize with indicator function degree 0
505 rowN
[p
] = 1.0; // all others are zero
507 // calculate up to degree p
508 for (sal_uInt32 s
= 1; s
<= p
; ++s
)
511 double fLeftFactor
= 0.0;
512 double fRightFactor
= ( u
[i
+1] - tk
) / ( u
[i
+1]- u
[i
-s
+1] );
513 // i-s "true index" - (i-p)"shift" = p-s
514 rowN
[p
-s
] = fRightFactor
* rowN
[p
-s
+1];
517 for (sal_uInt32 j
= s
-1; j
>=1 ; --j
)
519 fLeftFactor
= ( tk
- u
[i
-j
] ) / ( u
[i
-j
+s
] - u
[i
-j
] ) ;
520 fRightFactor
= ( u
[i
-j
+s
+1] - tk
) / ( u
[i
-j
+s
+1] - u
[i
-j
+1] );
521 // i-j "true index" - (i-p)"shift" = p-j
522 rowN
[p
-j
] = fLeftFactor
* rowN
[p
-j
] + fRightFactor
* rowN
[p
-j
+1];
526 fLeftFactor
= ( tk
- u
[i
] ) / ( u
[i
+s
] - u
[i
] );
527 // i "true index" - (i-p)"shift" = p
528 rowN
[p
] = fLeftFactor
* rowN
[p
];
532 } // anonymous namespace
534 // Calculates uniform parametric splines with subinterval length 1,
535 // according ODF1.2 part 1, chapter 'chart interpolation'.
536 void SplineCalculater::CalculateCubicSplines(
537 const drawing::PolyPolygonShape3D
& rInput
538 , drawing::PolyPolygonShape3D
& rResult
539 , sal_uInt32 nGranularity
)
541 OSL_PRECOND( nGranularity
> 0, "Granularity is invalid" );
543 rResult
.SequenceX
.realloc(0);
544 rResult
.SequenceY
.realloc(0);
545 rResult
.SequenceZ
.realloc(0);
547 sal_uInt32 nOuterCount
= rInput
.SequenceX
.getLength();
551 rResult
.SequenceX
.realloc(nOuterCount
);
552 rResult
.SequenceY
.realloc(nOuterCount
);
553 rResult
.SequenceZ
.realloc(nOuterCount
);
555 for( sal_uInt32 nOuter
= 0; nOuter
< nOuterCount
; ++nOuter
)
557 if( rInput
.SequenceX
[nOuter
].getLength() <= 1 )
558 continue; //we need at least two points
560 sal_uInt32 nMaxIndexPoints
= rInput
.SequenceX
[nOuter
].getLength()-1; // is >=1
561 const double* pOldX
= rInput
.SequenceX
[nOuter
].getConstArray();
562 const double* pOldY
= rInput
.SequenceY
[nOuter
].getConstArray();
563 const double* pOldZ
= rInput
.SequenceZ
[nOuter
].getConstArray();
565 ::std::vector
< double > aParameter(nMaxIndexPoints
+1);
567 for( sal_uInt32 nIndex
=1; nIndex
<=nMaxIndexPoints
; nIndex
++ )
569 aParameter
[nIndex
]=aParameter
[nIndex
-1]+1;
572 // Split the calculation to X, Y and Z coordinate
573 tPointVecType aInputX
;
574 aInputX
.resize(nMaxIndexPoints
+1);
575 tPointVecType aInputY
;
576 aInputY
.resize(nMaxIndexPoints
+1);
577 tPointVecType aInputZ
;
578 aInputZ
.resize(nMaxIndexPoints
+1);
579 for (sal_uInt32 nN
=0;nN
<=nMaxIndexPoints
; nN
++ )
581 aInputX
[ nN
].first
=aParameter
[nN
];
582 aInputX
[ nN
].second
=pOldX
[ nN
];
583 aInputY
[ nN
].first
=aParameter
[nN
];
584 aInputY
[ nN
].second
=pOldY
[ nN
];
585 aInputZ
[ nN
].first
=aParameter
[nN
];
586 aInputZ
[ nN
].second
=pOldZ
[ nN
];
589 // generate a spline for each coordinate. It holds the complete
590 // information to calculate each point of the curve
591 lcl_SplineCalculation
* aSplineX
;
592 lcl_SplineCalculation
* aSplineY
;
593 // lcl_SplineCalculation* aSplineZ; the z-coordinates of all points in
594 // a data series are equal. No spline calculation needed, but copy
595 // coordinate to output
597 if( pOldX
[ 0 ] == pOldX
[nMaxIndexPoints
] &&
598 pOldY
[ 0 ] == pOldY
[nMaxIndexPoints
] &&
599 pOldZ
[ 0 ] == pOldZ
[nMaxIndexPoints
] &&
600 nMaxIndexPoints
>=2 )
602 aSplineX
= new lcl_SplineCalculation( aInputX
) ;
603 aSplineY
= new lcl_SplineCalculation( aInputY
) ;
604 // aSplineZ = new lcl_SplineCalculation( aInputZ) ;
606 else // generate the kind "natural spline"
609 ::rtl::math::setInf( &fInfty
, false );
610 double fXDerivation
= fInfty
;
611 double fYDerivation
= fInfty
;
612 aSplineX
= new lcl_SplineCalculation( aInputX
, fXDerivation
, fXDerivation
);
613 aSplineY
= new lcl_SplineCalculation( aInputY
, fYDerivation
, fYDerivation
);
616 // fill result polygon with calculated values
617 rResult
.SequenceX
[nOuter
].realloc( nMaxIndexPoints
*nGranularity
+ 1);
618 rResult
.SequenceY
[nOuter
].realloc( nMaxIndexPoints
*nGranularity
+ 1);
619 rResult
.SequenceZ
[nOuter
].realloc( nMaxIndexPoints
*nGranularity
+ 1);
621 double* pNewX
= rResult
.SequenceX
[nOuter
].getArray();
622 double* pNewY
= rResult
.SequenceY
[nOuter
].getArray();
623 double* pNewZ
= rResult
.SequenceZ
[nOuter
].getArray();
625 sal_uInt32 nNewPointIndex
= 0; // Index in result points
627 for( sal_uInt32 ni
= 0; ni
< nMaxIndexPoints
; ni
++ )
629 // given point is surely a curve point
630 pNewX
[nNewPointIndex
] = pOldX
[ni
];
631 pNewY
[nNewPointIndex
] = pOldY
[ni
];
632 pNewZ
[nNewPointIndex
] = pOldZ
[ni
];
635 // calculate intermediate points
636 double fInc
= ( aParameter
[ ni
+1 ] - aParameter
[ni
] ) / static_cast< double >( nGranularity
);
637 for(sal_uInt32 nj
= 1; nj
< nGranularity
; nj
++)
639 double fParam
= aParameter
[ni
] + ( fInc
* static_cast< double >( nj
) );
641 pNewX
[nNewPointIndex
]=aSplineX
->GetInterpolatedValue( fParam
);
642 pNewY
[nNewPointIndex
]=aSplineY
->GetInterpolatedValue( fParam
);
643 // pNewZ[nNewPointIndex]=aSplineZ->GetInterpolatedValue( fParam );
644 pNewZ
[nNewPointIndex
] = pOldZ
[ni
];
649 pNewX
[nNewPointIndex
] = pOldX
[nMaxIndexPoints
];
650 pNewY
[nNewPointIndex
] = pOldY
[nMaxIndexPoints
];
651 pNewZ
[nNewPointIndex
] = pOldZ
[nMaxIndexPoints
];
658 // The implementation follows closely ODF1.2 spec, chapter chart:interpolation
659 // using the same names as in spec as far as possible, without prefix.
660 // More details can be found on
661 // Dr. C.-K. Shene: CS3621 Introduction to Computing with Geometry Notes
662 // Unit 9: Interpolation and Approximation/Curve Global Interpolation
663 // Department of Computer Science, Michigan Technological University
664 // http://www.cs.mtu.edu/~shene/COURSES/cs3621/NOTES/
665 // [last called 2011-05-20]
666 void SplineCalculater::CalculateBSplines(
667 const ::com::sun::star::drawing::PolyPolygonShape3D
& rInput
668 , ::com::sun::star::drawing::PolyPolygonShape3D
& rResult
669 , sal_uInt32 nResolution
670 , sal_uInt32 nDegree
)
672 // nResolution is ODF1.2 file format attribute chart:spline-resolution and
673 // ODF1.2 spec variable k. Causion, k is used as index in the spec in addition.
674 // nDegree is ODF1.2 file format attribute chart:spline-order and
675 // ODF1.2 spec variable p
676 OSL_ASSERT( nResolution
> 1 );
677 OSL_ASSERT( nDegree
>= 1 );
679 // limit the b-spline degree to prevent insanely large sets of points
680 sal_uInt32 p
= std::min
<sal_uInt32
>(nDegree
, MAX_BSPLINE_DEGREE
);
682 rResult
.SequenceX
.realloc(0);
683 rResult
.SequenceY
.realloc(0);
684 rResult
.SequenceZ
.realloc(0);
686 sal_Int32 nOuterCount
= rInput
.SequenceX
.getLength();
690 rResult
.SequenceX
.realloc(nOuterCount
);
691 rResult
.SequenceY
.realloc(nOuterCount
);
692 rResult
.SequenceZ
.realloc(nOuterCount
);
694 for( sal_Int32 nOuter
= 0; nOuter
< nOuterCount
; ++nOuter
)
696 if( rInput
.SequenceX
[nOuter
].getLength() <= 1 )
697 continue; // need at least 2 points, next piece of the series
699 // Copy input to vector of points and remove adjacent double points. The
700 // Z-coordinate is equal for all points in a series and holds the depth
701 // in 3D mode, simple copying is enough.
702 lcl_tSizeType nMaxIndexPoints
= rInput
.SequenceX
[nOuter
].getLength()-1; // is >=1
703 const double* pOldX
= rInput
.SequenceX
[nOuter
].getConstArray();
704 const double* pOldY
= rInput
.SequenceY
[nOuter
].getConstArray();
705 const double* pOldZ
= rInput
.SequenceZ
[nOuter
].getConstArray();
706 double fZCoordinate
= pOldZ
[0];
707 tPointVecType aPointsIn
;
708 aPointsIn
.resize(nMaxIndexPoints
+1);
709 for (lcl_tSizeType i
= 0; i
<= nMaxIndexPoints
; ++i
)
711 aPointsIn
[ i
].first
= pOldX
[i
];
712 aPointsIn
[ i
].second
= pOldY
[i
];
714 aPointsIn
.erase( ::std::unique( aPointsIn
.begin(), aPointsIn
.end()),
717 // n is the last valid index to the reduced aPointsIn
718 // There are n+1 valid data points.
719 const lcl_tSizeType n
= aPointsIn
.size() - 1;
721 continue; // need at least 2 points, degree p needs at least n+1 points
722 // next piece of series
724 boost::scoped_array
<double> t(new double [n
+1]);
725 if (!createParameterT(aPointsIn
, t
.get()))
727 continue; // next piece of series
730 lcl_tSizeType m
= n
+ p
+ 1;
731 boost::scoped_array
<double> u(new double [m
+1]);
732 createKnotVector(n
, p
, t
.get(), u
.get());
734 // The matrix N contains the B-spline basis functions applied to parameters.
735 // In each row only p+1 adjacent elements are non-zero. The starting
736 // column in a higher row is equal or greater than in the lower row.
737 // To store this matrix the non-zero elements are shifted to column 0
738 // and the amount of shifting is remembered in an array.
739 boost::scoped_array
<double*> aMatN(new double*[n
+1]);
740 for (lcl_tSizeType row
= 0; row
<=n
; ++row
)
742 aMatN
[row
] = new double[p
+1];
743 for (sal_uInt32 col
= 0; col
<= p
; ++col
)
744 aMatN
[row
][col
] = 0.0;
746 boost::scoped_array
<lcl_tSizeType
> aShift(new lcl_tSizeType
[n
+1]);
747 aMatN
[0][0] = 1.0; //all others are zero
751 for (lcl_tSizeType k
= 1; k
<=n
-1; ++k
)
752 { // all basis functions are applied to t_k,
753 // results are elements in row k in matrix N
755 // find the one interval with u_i <= t_k < u_(i+1)
756 // remember u_0 = ... = u_p = 0.0 and u_(m-p) = ... u_m = 1.0 and 0<t_k<1
758 while (!(u
[i
] <= t
[k
] && t
[k
] < u
[i
+1]))
763 // index in reduced matrix aMatN = (index in full matrix N) - (i-p)
766 applyNtoParameterT(i
, t
[k
], p
, u
.get(), aMatN
[k
]);
769 // Get matrix C of control points from the matrix equation aMatN * C = aPointsIn
770 // aPointsIn is overwritten with C.
771 // Gaussian elimination is possible without pivoting, see reference
772 lcl_tSizeType r
= 0; // true row index
773 lcl_tSizeType c
= 0; // true column index
774 double fDivisor
= 1.0; // used for diagonal element
775 double fEliminate
= 1.0; // used for the element, that will become zero
778 lcl_tSizeType nHelp
; // used in triangle change
779 bool bIsSuccessful
= true;
780 for (c
= 0 ; c
<= n
&& bIsSuccessful
; ++c
)
782 // search for first non-zero downwards
784 while ( r
< n
&& aMatN
[r
][c
-aShift
[r
]] == 0 )
788 if (aMatN
[r
][c
-aShift
[r
]] == 0.0)
790 // Matrix N is singular, although this is mathematically impossible
791 bIsSuccessful
= false;
795 // exchange total row r with total row c if necessary
798 for ( sal_uInt32 i
= 0; i
<= p
; ++i
)
801 aMatN
[r
][i
] = aMatN
[c
][i
];
804 aHelp
= aPointsIn
[r
];
805 aPointsIn
[r
] = aPointsIn
[c
];
806 aPointsIn
[c
] = aHelp
;
808 aShift
[r
] = aShift
[c
];
812 // divide row c, so that element(c,c) becomes 1
813 fDivisor
= aMatN
[c
][c
-aShift
[c
]]; // not zero, see above
814 for (sal_uInt32 i
= 0; i
<= p
; ++i
)
816 aMatN
[c
][i
] /= fDivisor
;
818 aPointsIn
[c
].first
/= fDivisor
;
819 aPointsIn
[c
].second
/= fDivisor
;
821 // eliminate forward, examine row c+1 to n-1 (worst case)
822 // stop if first non-zero element in row has an higher column as c
823 // look at nShift for that, elements in nShift are equal or increasing
824 for ( r
= c
+1; r
< n
&& aShift
[r
]<=c
; ++r
)
826 fEliminate
= aMatN
[r
][0];
827 if (fEliminate
!= 0.0) // else accidentally zero, nothing to do
829 for (sal_uInt32 i
= 1; i
<= p
; ++i
)
831 aMatN
[r
][i
-1] = aMatN
[r
][i
] - fEliminate
* aMatN
[c
][i
];
834 aPointsIn
[r
].first
-= fEliminate
* aPointsIn
[c
].first
;
835 aPointsIn
[r
].second
-= fEliminate
* aPointsIn
[c
].second
;
840 }// upper triangle form is reached
843 // eliminate backwards, begin with last column
844 for (lcl_tSizeType cc
= n
; cc
>= 1; --cc
)
846 // In row cc the diagonal element(cc,cc) == 1 and all elements left from
847 // diagonal are zero and do not influence other rows.
848 // Full matrix N has semibandwidth < p, therefore element(r,c) is
849 // zero, if abs(r-cc)>=p. abs(r-cc)=cc-r, because r<cc.
851 while ( r
!=0 && cc
-r
< p
)
853 fEliminate
= aMatN
[r
][ cc
- aShift
[r
] ];
854 if ( fEliminate
!= 0.0) // else element is accidentically zero, no action needed
856 // row r -= fEliminate * row cc only relevant for right side
857 aMatN
[r
][cc
- aShift
[r
]] = 0.0;
858 aPointsIn
[r
].first
-= fEliminate
* aPointsIn
[cc
].first
;
859 aPointsIn
[r
].second
-= fEliminate
* aPointsIn
[cc
].second
;
864 } // aPointsIn contains the control points now.
867 // calculate the intermediate points according given resolution
868 // using deBoor-Cox algorithm
869 lcl_tSizeType nNewSize
= nResolution
* n
+ 1;
870 rResult
.SequenceX
[nOuter
].realloc(nNewSize
);
871 rResult
.SequenceY
[nOuter
].realloc(nNewSize
);
872 rResult
.SequenceZ
[nOuter
].realloc(nNewSize
);
873 double* pNewX
= rResult
.SequenceX
[nOuter
].getArray();
874 double* pNewY
= rResult
.SequenceY
[nOuter
].getArray();
875 double* pNewZ
= rResult
.SequenceZ
[nOuter
].getArray();
876 pNewX
[0] = aPointsIn
[0].first
;
877 pNewY
[0] = aPointsIn
[0].second
;
878 pNewZ
[0] = fZCoordinate
; // Precondition: z-coordinates of all points of a series are equal
879 pNewX
[nNewSize
-1 ] = aPointsIn
[n
].first
;
880 pNewY
[nNewSize
-1 ] = aPointsIn
[n
].second
;
881 pNewZ
[nNewSize
-1 ] = fZCoordinate
;
882 boost::scoped_array
<double> aP(new double[m
+1]);
883 lcl_tSizeType nLow
= 0;
884 for ( lcl_tSizeType nTIndex
= 0; nTIndex
<= n
-1; ++nTIndex
)
886 for (sal_uInt32 nResolutionStep
= 1;
887 nResolutionStep
<= nResolution
&& !( nTIndex
== n
-1 && nResolutionStep
== nResolution
);
890 lcl_tSizeType nNewIndex
= nTIndex
* nResolution
+ nResolutionStep
;
891 double ux
= t
[nTIndex
] + nResolutionStep
* ( t
[nTIndex
+1] - t
[nTIndex
]) /nResolution
;
893 // get index nLow, so that u[nLow]<= ux < u[nLow +1]
894 // continue from previous nLow
895 while ( u
[nLow
] <= ux
)
902 for (lcl_tSizeType i
= nLow
-p
; i
<= nLow
; ++i
)
904 aP
[i
] = aPointsIn
[i
].first
;
906 for (sal_uInt32 lcl_Degree
= 1; lcl_Degree
<= p
; ++lcl_Degree
)
908 for (lcl_tSizeType i
= nLow
; i
>= nLow
+ lcl_Degree
- p
; --i
)
910 double fFactor
= ( ux
- u
[i
] ) / ( u
[i
+p
+1-lcl_Degree
] - u
[i
]);
911 aP
[i
] = (1 - fFactor
)* aP
[i
-1] + fFactor
* aP
[i
];
914 pNewX
[nNewIndex
] = aP
[nLow
];
917 for (lcl_tSizeType i
= nLow
- p
; i
<= nLow
; ++i
)
919 aP
[i
] = aPointsIn
[i
].second
;
921 for (sal_uInt32 lcl_Degree
= 1; lcl_Degree
<= p
; ++lcl_Degree
)
923 for (lcl_tSizeType i
= nLow
; i
>= nLow
+lcl_Degree
- p
; --i
)
925 double fFactor
= ( ux
- u
[i
] ) / ( u
[i
+p
+1-lcl_Degree
] - u
[i
]);
926 aP
[i
] = (1 - fFactor
)* aP
[i
-1] + fFactor
* aP
[i
];
929 pNewY
[nNewIndex
] = aP
[nLow
];
930 pNewZ
[nNewIndex
] = fZCoordinate
;
934 for (lcl_tSizeType row
= 0; row
<=n
; ++row
)
938 } // next piece of the series
943 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */