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 .
22 #include <tools/poly.hxx>
23 #include <boost/scoped_array.hpp>
25 #include <sgvspln.hxx>
30 /*.hlAppendix: C - programs*/
31 /*.hrConstants- and macrodefinitions*/
32 /*.fe The include file u_const.h should be stored in the directory, */
33 /*.fe where the compiler searches for include files. */
35 /*----------------------- FILE u_const.h ---------------------------*/
39 /* IEEE - standard for representation of floating-point numbers:
41 8 byte long floating point numbers with
43 53 bit mantissa ==> mantissa range: 2^52 different numbers
44 with 0.1 <= number < 1.0,
46 11 bit exponent ==> exponent range: -1024...+1023
48 The first line (#define IEEE) should be deleted if the machine
49 or the compiler does not use floating-point numbers according
50 to the IEEE standard. In which case also MAXEXPON, MINEXPON (see
51 below) should be adapted.
54 #ifdef IEEE /*-------------- if IEEE norm --------------------*/
56 #define MACH_EPS 2.220446049250313e-016 /* machine precision */
58 /* MACH_EPS is the smallest positive, by the machine representable
59 number x, which fulfills the equation: 1.0 + x > 1.0 */
61 #define MAXROOT 9.48075190810918e+153
63 #else /*------------------ otherwise--------------------*/
67 double pow (double,double);
70 double masch() /* calculate MACH_EPS machine independence */
72 double eps
= 1.0, x
= 2.0, y
= 1.0;
81 short basis() /* calculate BASE machine independence */
83 double x
= 1.0, one
= 1.0, b
= 1.0;
85 while ( (x
+ one
) - x
== one
) x
*= 2.0;
86 while ( (x
+ b
) == x
) b
*= 2.0;
88 return (short) ((x
+ b
) - x
);
91 #define BASIS basis() /* base of number representation */
93 /* If the machine (the compiler) does not use the IEEE-representation
94 for floating-point numbers, the next 2 constants should be adapted.
97 #define MAXEXPON 1023.0 /* largest exponent */
98 #define MINEXPON -1024.0 /* smallest exponent */
100 #define MACH_EPS masch()
102 #define POSMAX pow ((double) BASIS, MAXEXPON)
103 #define MAXROOT sqrt(POSMAX)
105 #endif /*-------------- END of ifdef --------------------*/
107 /* defines for function macros: */
109 #define abs(X) ((X) >= 0 ? (X) : -(X)) /* absolute number X */
110 #define sign(X, Y) (Y < 0 ? -abs(X) : abs(X)) /* sign of Y times */
113 /*-------------------- END of FILE u_const.h -----------------------*/
115 /*.HL appendix: C - programs*/
116 /*.HR Systems of equations for tridiagonal matrices*/
118 /*.FE P 3.7 tridiagonal systems of equations */
120 /*---------------------- MODULE tridiagonal -----------------------*/
122 sal_uInt16
TriDiagGS(bool rep
, sal_uInt16 n
, double* lower
,
123 double* diag
, double* upper
, double* b
)
124 /*************************/
125 /* Gaussian methods for */
126 /* tridiagonal matrices */
127 /*************************/
129 /*====================================================================*/
131 /* trdiag determines solution x of the system of linear equations */
132 /* A * x = b with tridiagonal n x n coefficient matrix A, which is */
133 /* stored in 3 vectors lower, upper and diag as per below: */
135 /* ( diag[0] upper[0] 0 0 . . . 0 ) */
136 /* ( lower[1] diag[1] upper[1] 0 . . . ) */
137 /* ( 0 lower[2] diag[2] upper[2] 0 . ) */
138 /* A = ( . 0 lower[3] . . . ) */
139 /* ( . . . . . 0 ) */
141 /* ( . . . upper[n-2] ) */
142 /* ( 0 . . . 0 lower[n-1] diag[n-1] ) */
144 /*====================================================================*/
148 /* Mainly for diagonal determinant triangular matrix, as they */
149 /* occur in Spline-interpolations. */
150 /* For diagonal dominant matrices always a left-upper row */
151 /* reduction exists; for non diagonal dominant triangular */
152 /* matrices we should pull forward the function band, as this */
153 /* works with row pivot searches, which is numerical more stable.*/
155 /*====================================================================*/
157 /* Input parameters: */
158 /* ================ */
159 /* n dimension of the matrix ( > 1 ) sal_uInt16 n */
161 /* lower lower antidiagonal double lower[n] */
162 /* diag main diagonal double diag[n] */
163 /* upper upper antidiagonal double upper[n] */
165 /* for rep = true lower, diag and upper contain the */
166 /* triangulation of the start matrix. */
168 /* b right side of equation double b[n] */
169 /* rep = false first call bool rep */
170 /* = true next call */
171 /* for the same matrix, */
172 /* but different b. */
174 /* Output parameters: */
175 /* ================= */
176 /* b solution vector of the system; double b[n] */
177 /* the original right side is overwritten */
179 /* lower ) contain for rep = false the decomposition of the */
180 /* diag ) matrix; the original values of the lower and */
181 /* upper ) diagonals are overwritten */
183 /* The determinant of the matrix is for rep = false defined by */
184 /* determinant A = diag[0] * ... * diag[n-1] */
189 /* = 1 n < 2 chosen */
190 /* = 2 triangular decomposition of matrix does not exist */
192 /*====================================================================*/
194 /* Functions used: */
195 /* =============== */
197 /* From the C library: fabs() */
199 /*====================================================================*/
206 // double fabs(double);
208 if ( n
< 2 ) return 1; /* n at least 2 */
210 /* if rep = false, */
213 /* decomposition of */
214 if (!rep
) /* matrix and determinant*/
216 for (i
= 1; i
< n
; i
++)
217 { if ( fabs(diag
[i
-1]) < MACH_EPS
) /* do not decompose */
218 return 2; /* if one diag[i] = 0 */
219 lower
[i
] /= diag
[i
-1];
220 diag
[i
] -= lower
[i
] * upper
[i
-1];
224 if ( fabs(diag
[n
-1]) < MACH_EPS
) return 2;
226 for (i
= 1; i
< n
; i
++) /* forward elimination */
227 b
[i
] -= lower
[i
] * b
[i
-1];
229 b
[n
-1] /= diag
[n
-1]; /* reverse elimination */
230 for (j
= n
-2; j
>= 0; j
--) {
232 b
[i
] = ( b
[i
] - upper
[i
] * b
[i
+1] ) / diag
[i
];
237 /*----------------------- END OF TRIDIAGONAL ------------------------*/
239 /*.HL Appendix: C - Programs*/
240 /*.HRSystems of equations with cyclic tridiagonal matrices*/
242 /*.FE P 3.8 Systems with cyclic tridiagonal matrices */
244 /*---------------- Module cyclic tridiagonal -----------------------*/
246 sal_uInt16
ZyklTriDiagGS(bool rep
, sal_uInt16 n
, double* lower
, double* diag
,
247 double* upper
, double* lowrow
, double* ricol
, double* b
)
248 /******************************/
249 /* Systems with cyclic */
250 /* tridiagonal matrices */
251 /******************************/
253 /*====================================================================*/
255 /* tzdiag determines the solution x of the linear equation system */
256 /* A * x = b with cyclic tridiagonal n x n coefficient- */
257 /* matrix A, which is stored in the 5 vectors: lower, upper, diag, */
258 /* lowrow and ricol as per below: */
260 /* ( diag[0] upper[0] 0 0 . . 0 ricol[0] ) */
261 /* ( lower[1] diag[1] upper[1] 0 . . 0 ) */
262 /* ( 0 lower[2] diag[2] upper[2] 0 . ) */
263 /* A = ( . 0 lower[3] . . . . ) */
264 /* ( . . . . . 0 ) */
266 /* ( 0 . . . upper[n-2] ) */
267 /* ( lowrow[0] 0 . . 0 lower[n-1] diag[n-1] ) */
269 /* Memory for lowrow[1],..,lowrow[n-3] und ricol[1],...,ricol[n-3] */
270 /* should be provided separately, as this should be available to */
271 /* store the decomposition matrix, which is overwritting */
272 /* the 5 vectors mentioned. */
274 /*====================================================================*/
278 /* Predominantly for diagonal dominant cyclic tridiagonal- */
279 /* matrices as they occur in spline-interpolations. */
280 /* For diagonal dominant matices only a LU-decomposition exists. */
282 /*====================================================================*/
284 /* Input parameters: */
285 /* ================= */
286 /* n Dimension of the matrix ( > 2 ) sal_uInt16 n */
287 /* lower lower antidiagonal double lower[n] */
288 /* diag main diagonal double diag[n] */
289 /* upper upper antidiagonal double upper[n] */
290 /* b right side of the system double b[n] */
291 /* rep = FALSE first call bool rep */
292 /* = TRUE repeated call */
293 /* for equal matrix, */
294 /* but different b. */
296 /* Output parameters: */
297 /* ================== */
298 /* b solution vector of the system, double b[n] */
299 /* the original right side is overwritten */
301 /* lower ) contain for rep = false the solution of the matrix;*/
302 /* diag ) the original values of lower and diagonal will be */
303 /* upper ) overwritten */
304 /* lowrow ) double lowrow[n-2] */
305 /* ricol ) double ricol[n-2] */
307 /* The determinant of the matrix is for rep = false */
308 /* det A = diag[0] * ... * diag[n-1] defined . */
313 /* = 1 n < 3 chosen */
314 /* = 2 Decomposition matrix does not exist */
316 /*====================================================================*/
318 /* Used functions: */
319 /* =============== */
321 /* from the C library: fabs() */
323 /*====================================================================*/
327 double temp
; // fabs(double);
331 if ( n
< 3 ) return 1;
333 if (!rep
) /* If rep = false, */
334 { /* calculate decomposition */
335 lower
[0] = upper
[n
-1] = 0.0; /* of the matrix. */
337 if ( fabs (diag
[0]) < MACH_EPS
) return 2;
338 /* Do not decompose if the */
339 temp
= 1.0 / diag
[0]; /* value of a diagonal */
340 upper
[0] *= temp
; /* element is smaller then */
341 ricol
[0] *= temp
; /* MACH_EPS */
343 for (i
= 1; i
< n
-2; i
++)
344 { diag
[i
] -= lower
[i
] * upper
[i
-1];
345 if ( fabs(diag
[i
]) < MACH_EPS
) return 2;
346 temp
= 1.0 / diag
[i
];
348 ricol
[i
] = -lower
[i
] * ricol
[i
-1] * temp
;
351 diag
[n
-2] -= lower
[n
-2] * upper
[n
-3];
352 if ( fabs(diag
[n
-2]) < MACH_EPS
) return 2;
354 for (i
= 1; i
< n
-2; i
++)
355 lowrow
[i
] = -lowrow
[i
-1] * upper
[i
-1];
357 lower
[n
-1] -= lowrow
[n
-3] * upper
[n
-3];
358 upper
[n
-2] = ( upper
[n
-2] - lower
[n
-2] * ricol
[n
-3] ) / diag
[n
-2];
360 for (temp
= 0.0, i
= 0; i
< n
-2; i
++)
361 temp
-= lowrow
[i
] * ricol
[i
];
362 diag
[n
-1] += temp
- lower
[n
-1] * upper
[n
-2];
364 if ( fabs(diag
[n
-1]) < MACH_EPS
) return 2;
367 b
[0] /= diag
[0]; /* forward elimination */
368 for (i
= 1; i
< n
-1; i
++)
369 b
[i
] = ( b
[i
] - b
[i
-1] * lower
[i
] ) / diag
[i
];
371 for (temp
= 0.0, i
= 0; i
< n
-2; i
++)
372 temp
-= lowrow
[i
] * b
[i
];
374 b
[n
-1] = ( b
[n
-1] + temp
- lower
[n
-1] * b
[n
-2] ) / diag
[n
-1];
376 b
[n
-2] -= b
[n
-1] * upper
[n
-2]; /* backward elimination */
377 for (j
= n
-3; j
>= 0; j
--) {
379 b
[i
] -= upper
[i
] * b
[i
+1] + ricol
[i
] * b
[n
-1];
384 /*------------------ END of CYCLIC TRIDIAGONAL ---------------------*/
388 // Calculates the coefficients of natural cubic splines with n intervals.
389 sal_uInt16
NaturalSpline(sal_uInt16 n
, double* x
, double* y
,
390 double Marg0
, double MargN
,
392 double* b
, double* c
, double* d
)
395 boost::scoped_array
<double> a
;
396 boost::scoped_array
<double> h
;
400 if ( (MargCond
& ~3) ) return 2;
401 a
.reset(new double[n
+1]);
402 h
.reset(new double[n
+1]);
405 if (h
[i
]<=0.0) return 1;
407 for (i
=0;i
<n
-1;i
++) {
408 a
[i
]=3.0*((y
[i
+2]-y
[i
+1])/h
[i
+1]-(y
[i
+1]-y
[i
])/h
[i
]);
411 d
[i
]=2.0*(h
[i
]+h
[i
+1]);
419 a
[0] =a
[0]*h
[1]/(h
[0]+h
[1]);
420 a
[n
-2]=a
[n
-2]*h
[n
-2]/(h
[n
-1]+h
[n
-2]);
422 d
[n
-2]=d
[n
-2]-h
[n
-1];
424 b
[n
-2]=b
[n
-2]-h
[n
-1];
428 a
[0] =a
[0]-1.5*((y
[1]-y
[0])/h
[0]-Marg0
);
429 a
[n
-2]=a
[n
-2]-1.5*(MargN
-(y
[n
]-y
[n
-1])/h
[n
-1]);
431 d
[n
-2]=d
[n
-2]-h
[n
-1]*0.5;
434 a
[0] =a
[0]-h
[0]*Marg0
*0.5;
435 a
[n
-2]=a
[n
-2]-h
[n
-1]*MargN
*0.5;
438 a
[0] =a
[0]+Marg0
*h
[0]*h
[0]*0.5;
439 a
[n
-2]=a
[n
-2]-MargN
*h
[n
-1]*h
[n
-1]*0.5;
441 d
[n
-2]=d
[n
-2]+h
[n
-1];
447 error
=TriDiagGS(false,n
-1,b
,d
,c
,a
.get());
448 if (error
!=0) return error
+2;
449 for (i
=0;i
<n
-1;i
++) c
[i
+1]=a
[i
];
457 c
[0]=c
[1]+h
[0]*(c
[1]-c
[2])/h
[1];
458 c
[n
]=c
[n
-1]+h
[n
-1]*(c
[n
-1]-c
[n
-2])/h
[n
-2];
462 c
[0]=1.5*((y
[1]-y
[0])/h
[0]-Marg0
);
463 c
[0]=(c
[0]-c
[1]*h
[0]*0.5)/h
[0];
464 c
[n
]=1.5*((y
[n
]-y
[n
-1])/h
[n
-1]-MargN
);
465 c
[n
]=(c
[n
]-c
[n
-1]*h
[n
-1]*0.5)/h
[n
-1];
472 c
[0]=c
[1]-Marg0
*h
[0]*0.5;
473 c
[n
]=c
[n
-1]+MargN
*h
[n
-1]*0.5;
477 b
[i
]=(y
[i
+1]-y
[i
])/h
[i
]-h
[i
]*(c
[i
+1]+2.0*c
[i
])/3.0;
478 d
[i
]=(c
[i
+1]-c
[i
])/(3.0*h
[i
]);
483 // calculates the coefficients of periodical cubic splines with n intervals.
484 sal_uInt16
PeriodicSpline(sal_uInt16 n
, double* x
, double* y
,
485 double* b
, double* c
, double* d
)
486 { // array dimensions should range from [0..n]!
488 sal_uInt16 i
,im1
,nm1
; //integer
490 boost::scoped_array
<double> a
;
491 boost::scoped_array
<double> lowrow
;
492 boost::scoped_array
<double> ricol
;
496 for (i
=0;i
<=nm1
;i
++) if (x
[i
+1]<=x
[i
]) return 2; // should be strictly monotonically decreasing!
497 if (y
[n
]!=y
[0]) return 3; // begin and end should be equal!
499 a
.reset(new double[n
+1]);
500 lowrow
.reset(new double[n
+1]);
501 ricol
.reset(new double[n
+1]);
504 c
[1]=3.0*((y
[2]-y
[1])/(x
[2]-x
[1]));
505 c
[1]=c
[1]-3.0*((y
[i
]-y
[0])/(x
[1]-x
[0]));
506 c
[1]=c
[1]/(x
[2]-x
[0]);
509 for (i
=1;i
<=nm1
;i
++) {
516 a
[im1
]=3.0*((y
[i
+1]-y
[i
])/hr
-(y
[i
]-y
[im1
])/hl
);
524 a
[nm1
]=3.0*((y
[1]-y
[0])/hr
-(y
[n
]-y
[nm1
])/hl
);
525 Error
=ZyklTriDiagGS(false,n
,b
,d
,c
,lowrow
.get(),ricol
.get(),a
.get());
530 for (i
=0;i
<=nm1
;i
++) c
[i
+1]=a
[i
];
533 for (i
=0;i
<=nm1
;i
++) {
535 b
[i
]=(y
[i
+1]-y
[i
])/hl
;
536 b
[i
]=b
[i
]-hl
*(c
[i
+1]+2.0*c
[i
])/3.0;
537 d
[i
]=(c
[i
+1]-c
[i
])/hl
/3.0;
542 // calculate the coefficients of parametric natural of periodical cubic splines
544 sal_uInt16
ParaSpline(sal_uInt16 n
, double* x
, double* y
, sal_uInt8 MargCond
,
545 double Marg01
, double Marg02
,
546 double MargN1
, double MargN2
,
547 bool CondT
, double* T
,
548 double* bx
, double* cx
, double* dx
,
549 double* by
, double* cy
, double* dy
)
553 double deltX
,deltY
,delt
,
558 if ((MargCond
& ~3) && (MargCond
!= 4)) return 2; // invalid boundary condition
562 deltX
=x
[i
+1]-x
[i
]; deltY
=y
[i
+1]-y
[i
];
563 delt
=deltX
*deltX
+deltY
*deltY
;
564 if (delt
<=0.0) return 3; // two identical adjacent points!
565 T
[i
+1]=T
[i
]+sqrt(delt
);
571 alphX
=Marg01
; betX
=MargN1
;
572 alphY
=Marg02
; betY
=MargN2
;
575 if (x
[n
]!=x
[0]) return 3;
576 if (y
[n
]!=y
[0]) return 4;
579 if (abs(Marg01
)>=MAXROOT
) {
581 alphY
=sign(1.0,y
[1]-y
[0]);
583 alphX
=sign(sqrt(1.0/(1.0+Marg01
*Marg01
)),x
[1]-x
[0]);
586 if (abs(MargN1
)>=MAXROOT
) {
588 betY
=sign(1.0,y
[n
]-y
[n
-1]);
590 betX
=sign(sqrt(1.0/(1.0+MargN1
*MargN1
)),x
[n
]-x
[n
-1]);
596 Error
=PeriodicSpline(n
,T
,x
,bx
,cx
,dx
);
597 if (Error
!=0) return Error
+4;
598 Error
=PeriodicSpline(n
,T
,y
,by
,cy
,dy
);
599 if (Error
!=0) return Error
+10;
601 Error
=NaturalSpline(n
,T
,x
,alphX
,betX
,MargCond
,bx
,cx
,dx
);
602 if (Error
!=0) return Error
+4;
603 Error
=NaturalSpline(n
,T
,y
,alphY
,betY
,MargCond
,by
,cy
,dy
);
604 if (Error
!=0) return Error
+9;
609 bool CalcSpline(Polygon
& rPoly
, bool Periodic
, sal_uInt16
& n
,
610 double*& ax
, double*& ay
, double*& bx
, double*& by
,
611 double*& cx
, double*& cy
, double*& dx
, double*& dy
, double*& T
)
615 double MargN1
,MargN2
;
617 Point
P0(-32768,-32768);
621 ax
=new double[rPoly
.GetSize()+2];
622 ay
=new double[rPoly
.GetSize()+2];
625 for (i
=0;i
<rPoly
.GetSize();i
++) {
626 Pt
=rPoly
.GetPoint(i
);
627 if (i
==0 || Pt
!=P0
) {
655 if (n
>0) n
--; // correct n (number of partial polynoms)
658 if ( ( Marg
== 3 && n
>= 3 ) || ( Marg
== 2 && n
>= 2 ) )
660 bRet
= ParaSpline(n
,ax
,ay
,Marg
,Marg01
,Marg01
,MargN1
,MargN2
,false,T
,bx
,cx
,dx
,by
,cy
,dy
) == 0;
678 bool Spline2Poly(Polygon
& rSpln
, bool Periodic
, Polygon
& rPoly
)
680 short MinKoord
=-32000; // to prevent
681 short MaxKoord
=32000; // overflows
683 double* ax
; // coefficients of the polynoms
693 double Step
; // stepsize for t
694 double dt1
,dt2
,dt3
; // delta t, y, ^3
695 sal_uInt16 n
; // number of partial polynoms to draw
696 sal_uInt16 i
; // actual partial polynom
697 bool bOk
; // all still ok?
698 sal_uInt16 PolyMax
=16380; // max number of polygon points
700 bOk
=CalcSpline(rSpln
,Periodic
,n
,ax
,ay
,bx
,by
,cx
,cy
,dx
,dy
,tv
);
705 rPoly
.SetPoint(Point(short(ax
[0]),short(ay
[0])),0); // first point
707 while (i
<n
) { // draw n partial polynoms
709 bool bEnd
=false; // partial polynom ended?
710 while (!bEnd
) { // extrapolate one partial polynom
713 dt1
=t
-tv
[i
]; dt2
=dt1
*dt1
; dt3
=dt2
*dt1
;
714 long x
=long(ax
[i
]+bx
[i
]*dt1
+cx
[i
]*dt2
+dx
[i
]*dt3
);
715 long y
=long(ay
[i
]+by
[i
]*dt1
+cy
[i
]*dt2
+dy
[i
]*dt3
);
716 if (x
<MinKoord
) x
=MinKoord
; if (x
>MaxKoord
) x
=MaxKoord
;
717 if (y
<MinKoord
) y
=MinKoord
; if (y
>MaxKoord
) y
=MaxKoord
;
718 if (rPoly
.GetSize()<PolyMax
) {
719 rPoly
.SetSize(rPoly
.GetSize()+1);
720 rPoly
.SetPoint(Point(short(x
),short(y
)),rPoly
.GetSize()-1);
722 bOk
=false; // error: polygon becomes to large
725 } // end of partial polynom
726 i
++; // next partial polynom
743 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */