BSplineCurves.Mod 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. MODULE BSplineCurves;
  2. (**
  3. AUTHOR "Alexey Morozov";
  4. PURPOSE "Parametric curves in B-Spline representation";
  5. This software is implemented on the base of the work
  6. M.Unser, "Splines: A Perfect Fit for Signal and Image Processing," IEEE Signal Processing Magazine, vol. 16, no. 6, pp. 22-38, November 1999
  7. *)
  8. IMPORT
  9. MathL, KernelLog;
  10. CONST
  11. (* supported boundary conventions *)
  12. MirrorW = 0;
  13. Periodic = 1;
  14. Tolerance = 1.0D-9; (* default boundary handling tolerance *)
  15. TYPE
  16. Buffer = OBJECT
  17. VAR x: ARRAY [*] OF LONGREAL;
  18. PROCEDURE &Init(len: LONGINT); BEGIN NEW(x,len); END Init;
  19. PROCEDURE Length(): LONGINT; BEGIN RETURN LEN(x,0); END Length;
  20. END Buffer;
  21. (*
  22. Direct B-Spline transform used for the transformation of data to the B-Spline domain
  23. *)
  24. DirectTransform = OBJECT
  25. VAR
  26. poles: ARRAY [*] OF LONGREAL;
  27. gain: LONGREAL;
  28. boundary: LONGINT;
  29. accelBuf: ARRAY [*] OF Buffer;
  30. (*
  31. Direct transform construction
  32. degree - B-Spline degree
  33. boundary - boundary handling convention
  34. tolerance - tolerance for handling the boundaries
  35. *)
  36. PROCEDURE &Init(degree, boundary: LONGINT; tolerance: LONGREAL);
  37. VAR
  38. i, j, n: LONGINT;
  39. p, v, w: LONGREAL;
  40. buf: Buffer;
  41. BEGIN
  42. GetDirectFilter(degree,poles,gain);
  43. (* prepare acceleration of the transform *)
  44. NEW(accelBuf,LEN(poles,0));
  45. v := MathL.ln(ABS(tolerance));
  46. FOR i := 0 TO LEN(poles,0)-1 DO
  47. p := poles[i];
  48. n := ENTIER(0.5D0 + (v / MathL.ln( ABS( p ) )) );
  49. NEW(buf,n); accelBuf[i] := buf; w := 1.0D0;
  50. FOR j := 0 TO n-1 DO buf.x[j] := w; w := w*p; END;
  51. END;
  52. SELF.boundary := boundary;
  53. END Init;
  54. PROCEDURE InitCausal(iPole: LONGINT; CONST s: ARRAY [*] OF LONGREAL): LONGREAL;
  55. VAR
  56. buf: Buffer;
  57. i: LONGINT;
  58. ic, z, zn, z2n, iz: LONGREAL;
  59. BEGIN
  60. CASE boundary OF
  61. |MirrorW:
  62. buf := accelBuf[iPole];
  63. IF buf.Length() < LEN(s,0) THEN (* acelerated computation *)
  64. ic := s[0..buf.Length()-1] +* buf.x;
  65. ELSE (* full loop *)
  66. z := poles[iPole];
  67. zn := z; iz := 1.0D0 / z;
  68. z2n := IPower(ABS(z),LEN(s,0)-1);
  69. IF (z < 0) & ODD( LEN(s,0) - 1 ) THEN z2n := -z2n; END;
  70. ic := s[0] + z2n * s[LEN(s,0) - 1]; z2n := z2n * z2n * iz;
  71. FOR i := 1 TO LEN(s,0) - 2 DO ic := ic + (zn + z2n) * s[i]; zn := zn * z; z2n := z2n * iz; END;
  72. ic := ic / (1.0D0 - zn * zn);
  73. END;
  74. |Periodic:
  75. buf := accelBuf[iPole];
  76. IF buf.Length() < LEN(s,0) THEN (* acelerated computation *)
  77. i := LEN(s,0)-buf.Length()+1;
  78. ic := s[0] + s[LEN(s,0)-1..i BY -1] +* buf.x[1..MAX];
  79. ic := ic / (1.0D0 - IPower(z,LEN(s,0)));
  80. ELSE (* full loop *)
  81. z := poles[iPole];
  82. zn := IPower(z,LEN(s,0) - 1);
  83. iz := 1.0D0 / z;
  84. ic := s[0];
  85. FOR i := 1 TO LEN(s,0)-1 DO
  86. ic := ic + zn * s[i];
  87. zn := zn * iz;
  88. END;
  89. ic := ic / (1.0D0 - IPower(z,LEN(s,0)));
  90. END;
  91. ELSE
  92. HALT(100);
  93. END;
  94. RETURN ic;
  95. END InitCausal;
  96. PROCEDURE InitAnticausal(iPole: LONGINT; CONST s: ARRAY [*] OF LONGREAL): LONGREAL;
  97. VAR
  98. buf: Buffer;
  99. i: LONGINT;
  100. ic, z, zn: LONGREAL;
  101. BEGIN
  102. CASE boundary OF
  103. |MirrorW:
  104. z := poles[iPole];
  105. ic := z * (s[LEN(s,0)-1] + z * s[LEN(s,0)-2]) / (z * z - 1);
  106. |Periodic:
  107. buf := accelBuf[iPole];
  108. z := poles[iPole];
  109. IF buf.Length() < LEN(s,0) THEN (* acelerated computation *)
  110. ic := s[0] + s[LEN(s,0)-1] / z;
  111. ic := ic + s[1..buf.Length()-1] +* buf.x[1..MAX];
  112. ic := ic * z * z / (IPower(z,LEN(s,0)) - 1.0D0);
  113. ELSE (* full loop *)
  114. ic := s[0] + s[LEN(s,0)-1] / z; zn := z;
  115. FOR i := 1 TO LEN(s,0)-2 DO
  116. ic := ic + zn * s[i]; zn := zn * z;
  117. END;
  118. ic := ic * z * z / (IPower(z,LEN(s,0)) - 1.0D0);
  119. END;
  120. END;
  121. RETURN ic;
  122. END InitAnticausal;
  123. (*
  124. Transform data to B-Spline domain
  125. s - input data array [N]
  126. c - output array of B-Spline coefficients [N]
  127. *)
  128. PROCEDURE Transform(CONST s: ARRAY [*] OF LONGREAL; VAR c: ARRAY [*] OF LONGREAL);
  129. VAR
  130. i, j: LONGINT;
  131. z: LONGREAL;
  132. BEGIN
  133. IF LEN(c,0) # LEN(s,0) THEN NEW(c,LEN(s,0)); END;
  134. c := s*gain;
  135. FOR i := 0 TO LEN(poles,0)-1 DO
  136. z := poles[i];
  137. c[0] := InitCausal(i,c);
  138. FOR j := 1 TO LEN(c,0) - 1 DO c[j] := c[j] + z*c[j-1]; END;
  139. c[LEN(c,0)-1] := InitAnticausal(i,c);
  140. FOR j := LEN(c,0)-2 TO 0 BY -1 DO c[j] := z*(c[j+1] - c[j]); END;
  141. END;
  142. END Transform;
  143. END DirectTransform;
  144. (** A parametric B-Spline curve *)
  145. Curve* = OBJECT
  146. VAR
  147. degree-: LONGINT; (** B-Spline degree *)
  148. closed-: BOOLEAN; (** TRUE for closed curves *)
  149. kx-, ky-: ARRAY [*] OF LONGREAL; (** curve knots *)
  150. boundary: LONGINT;
  151. direct: DirectTransform;
  152. cx, cy: ARRAY [*] OF LONGREAL; (* B-Spline coefficieints for x(t) and y(t) corresponding to the knots *)
  153. dt: ARRAY [*] OF LONGREAL;
  154. (**
  155. Construct a B-Spline curve
  156. degree - B-Spline degree
  157. closed - TRUE for closed curve
  158. *)
  159. PROCEDURE &Init*(degree: LONGINT; closed: BOOLEAN);
  160. BEGIN
  161. IF closed THEN boundary := Periodic; ELSE boundary := MirrorW; END;
  162. IF degree > 1 THEN
  163. NEW(direct,degree,boundary,Tolerance);
  164. END;
  165. SELF.degree := degree;
  166. SELF.closed := closed;
  167. END Init;
  168. (**
  169. Set knots determining the curve
  170. knotsX, knotsY - x and y coordinates of the knots, in relative units
  171. *)
  172. PROCEDURE SetKnots*(CONST knotsX, knotsY: ARRAY [*] OF LONGREAL);
  173. BEGIN
  174. ASSERT(LEN(knotsX,0) = LEN(knotsY,0));
  175. IF degree > 1 THEN
  176. direct.Transform(knotsX,cx);
  177. direct.Transform(knotsY,cy);
  178. ELSE
  179. cx := knotsX;
  180. cy := knotsY;
  181. END;
  182. kx := knotsX;
  183. ky := knotsY;
  184. IF closed THEN
  185. IF LEN(dt,0) # LEN(kx,0) THEN NEW(dt,LEN(kx,0)); END;
  186. ELSE
  187. IF LEN(dt,0) # LEN(kx,0)-1 THEN NEW(dt,LEN(kx,0)-1); END;
  188. END;
  189. END SetKnots;
  190. (**
  191. Evaluate the points of the curve at a set of given parameter values
  192. t - input array of parameter values
  193. x, y - output point coordinates
  194. *)
  195. PROCEDURE Eval*(CONST t: ARRAY [*] OF LONGREAL; VAR x, y: ARRAY [*] OF LONGREAL);
  196. BEGIN
  197. IF LEN(x,0) # LEN(t,0) THEN NEW(x,LEN(t,0)); END;
  198. IF LEN(y,0) # LEN(t,0) THEN NEW(y,LEN(t,0)); END;
  199. Interpolate(degree,0,1,boundary,cx,t,x);
  200. Interpolate(degree,0,1,boundary,cy,t,y);
  201. END Eval;
  202. (**
  203. Get polyline used for rendering of the curve
  204. maxDist - maximal distance between the output polyline points, in relative units
  205. x, y - output set of points, in the same relative units
  206. *)
  207. PROCEDURE GetPoly*(maxDist: LONGREAL; VAR x, y: ARRAY [*] OF LONGREAL);
  208. VAR
  209. i, j, k, m, n: LONGINT;
  210. d1, d2, p, t: LONGREAL;
  211. BEGIN
  212. (*
  213. determine number of output points
  214. *)
  215. n := LEN(kx,0); (* account original knots *)
  216. FOR i := 0 TO LEN(kx,0)-2 DO
  217. (* distance between subsequent knots *)
  218. d1 := kx[i+1]-kx[i];
  219. d2 := ky[i+1]-ky[i];
  220. d1 := MathL.sqrt(d1*d1 + d2*d2);
  221. (* number of subintervals within the interval *)
  222. m := ENTIER(0.5D0 + (d1/ maxDist)) - 1;
  223. IF m > 1 THEN
  224. dt[i] := 1.0D0 / m;
  225. INC(n,m-1);
  226. ELSE
  227. dt[i] := 1.0D0;
  228. END;
  229. END;
  230. IF closed THEN (* additional interval for closed curves *)
  231. (* distance between last and first knots *)
  232. d1 := kx[0]-kx[LEN(kx,0)-1];
  233. d2 := ky[0]-ky[LEN(kx,0)-1];
  234. d1 := MathL.sqrt(d1*d1 + d2*d2);
  235. (* number of subintervals within the interval *)
  236. m := ENTIER(0.5D0 + (d1/ maxDist)) - 1;
  237. IF m > 1 THEN
  238. dt[LEN(dt,0)-1] := 1.0D0 / m;
  239. INC(n,m); (* add 1 for rendering the first knot to close the curve *)
  240. ELSE
  241. dt[LEN(dt,0)-1] := 1.0D0; INC(n);
  242. END;
  243. END;
  244. IF LEN(x,0) # n THEN NEW(x,n); END;
  245. IF LEN(y,0) # n THEN NEW(y,n); END;
  246. IF n > LEN(dt,0)+1 THEN
  247. j := 0;
  248. FOR i := 0 TO LEN(dt,0)-1 DO
  249. p := dt[i];
  250. m := ENTIER(0.5D0 + 1.0D0/p);
  251. t := i;
  252. FOR k := 0 TO m-1 DO
  253. y[j] := t; INC(j);
  254. t := t + p;
  255. END;
  256. END;
  257. ASSERT(j = n-1);
  258. y[j] := LEN(dt,0);
  259. Eval(y,x,y);
  260. ELSE
  261. x[0..LEN(kx,0)-1] := kx;
  262. y[0..LEN(ky,0)-1] := ky;
  263. IF closed THEN
  264. x[LEN(x,0)-1] := kx[0];
  265. y[LEN(y,0)-1] := ky[0];
  266. END;
  267. END;
  268. END GetPoly;
  269. (**
  270. Length of the curve, in relative units
  271. *)
  272. PROCEDURE Length*(): LONGREAL;
  273. BEGIN
  274. RETURN 0; (* not yet implemented *)
  275. END Length;
  276. (**
  277. Area occupied by the closed curve, in relative units
  278. *)
  279. PROCEDURE Area*(): LONGREAL;
  280. BEGIN
  281. RETURN 0; (* not yet implemented *)
  282. END Area;
  283. END Curve;
  284. (*
  285. Get pole-based representation of the symmetric IIR filter used for the direct transformation filtering
  286. degree - B-Spline degree
  287. poles - array of the filter poles
  288. gain - gain of the filter
  289. *)
  290. PROCEDURE GetDirectFilter(degree: LONGINT; VAR poles: ARRAY [*] OF LONGREAL; VAR gain: LONGREAL);
  291. VAR
  292. i: LONGINT;
  293. BEGIN
  294. CASE degree OF
  295. |2: NEW(poles,1); poles[0] := MathL.sqrt( 8.0D0 ) - 3.0D0;
  296. |3: NEW(poles,1); poles[0] := MathL.sqrt( 3.0D0 ) - 2.0D0;
  297. |4:
  298. NEW(poles,2);
  299. poles[0] := MathL.sqrt(664.0D0 - MathL.sqrt(438976.0D0)) + MathL.sqrt(304.0D0) - 19.0D0;
  300. poles[1] := MathL.sqrt(664.0D0 + MathL.sqrt(438976.0D0)) - MathL.sqrt(304.0D0) - 19.0D0;
  301. |5:
  302. NEW(poles,2);
  303. poles[0] := MathL.sqrt(135.0D0 / 2.0D0 - MathL.sqrt(17745.0D0 / 4.0D0)) + MathL.sqrt(105.0D0 / 4.0D0)- 13.0D0 / 2.0D0;
  304. poles[1] := MathL.sqrt(135.0D0 / 2.0D0 + MathL.sqrt(17745.0D0 / 4.0D0)) - MathL.sqrt(105.0D0 / 4.0D0)- 13.0D0 / 2.0D0;
  305. |6:
  306. NEW(poles,3);
  307. poles[0] := -0.48829458930304475513011803888378906211227916123938D0;
  308. poles[1] := -0.081679271076237512597937765737059080653379610398148D0;
  309. poles[2] := -0.0014141518083258177510872439765585925278641690553467D0;
  310. |7:
  311. NEW(poles,3);
  312. poles[0] := -0.53528043079643816554240378168164607183392315234269D0;
  313. poles[1] := -0.12255461519232669051527226435935734360548654942730D0;
  314. poles[2] := -0.0091486948096082769285930216516478534156925639545994D0;
  315. |8:
  316. NEW(poles,4);
  317. poles[0] := -0.57468690924876543053013930412874542429066157804125D0;
  318. poles[1] := -0.16303526929728093524055189686073705223476814550830D0;
  319. poles[2] := -0.023632294694844850023403919296361320612665920854629D0;
  320. poles[3] := -0.00015382131064169091173935253018402160762964054070043D0;
  321. |9:
  322. NEW(poles,4);
  323. poles[0] := -0.60799738916862577900772082395428976943963471853991D0;
  324. poles[1] := -0.20175052019315323879606468505597043468089886575747D0;
  325. poles[2] := -0.043222608540481752133321142979429688265852380231497D0;
  326. poles[3] := -0.0021213069031808184203048965578486234220548560988624D0;
  327. |10:
  328. NEW(poles,5);
  329. poles[0] := -0.63655066396942385875799205491349773313787959010128860432339D0;
  330. poles[1] := -0.238182798377573284887456162200161978666543494059728787251924D0;
  331. poles[2] := -0.065727033228308551538201803949684252205121694392255863103034D0;
  332. poles[3] := -0.0075281946755486906437698340318148831650567567441314086093636D0;
  333. poles[4] := -0.0000169827628232746642307274679399688786114400132341362095006930D0;
  334. |11:
  335. NEW(poles,5);
  336. poles[0] := -0.66126606890073470691013126292248166961816286716800880802421D0;
  337. poles[1] := -0.272180349294785885686295280258287768151235259565335176244192D0;
  338. poles[2] := -0.089759599793713309944142676556141542547561966017018544406214D0;
  339. poles[3] := -0.0166696273662346560965858360898150837154727205519335156053610D0;
  340. poles[4] := -0.00051055753444650205713591952840749392417989252534014106289610D0;
  341. ELSE
  342. (* kernels of any order can be constructed recursively:
  343. B-Spline Signal Processing: Part I - Theory by Michael Unser
  344. *)
  345. KernelLog.String("Unsupported B-spline degree!"); KernelLog.Ln;
  346. HALT(100);
  347. END;
  348. gain := 1.0D0;
  349. FOR i := 0 TO LEN(poles,0) - 1 DO gain := gain * (1.0D0 - poles[i]) * (1.0D0 - 1.0D0 / poles[i]); END;
  350. END GetDirectFilter;
  351. (*
  352. B-Spline interpolation
  353. degree - B-Spline degree
  354. x0 - origin of the domain
  355. dx - step of the grid
  356. boundary - boundary handling convention
  357. c - array of B-Spline coefficients
  358. x - array of sample locations where to interpolate
  359. v - output array of interpolated values
  360. *)
  361. PROCEDURE Interpolate(degree: LONGINT; x0, dx: LONGREAL; boundary: LONGINT; CONST c: ARRAY [*] OF LONGREAL; CONST x: ARRAY [*] OF LONGREAL; VAR v: ARRAY [*] OF LONGREAL);
  362. VAR
  363. i, j, k, n: LONGINT;
  364. s, s2, s4, t, t0, t1, idx: LONGREAL;
  365. xx: LONGREAL;
  366. ind0: ARRAY 10 OF LONGINT;
  367. w0: ARRAY 10 OF LONGREAL;
  368. BEGIN
  369. idx := 1.0D0/dx;
  370. FOR k := 0 TO LEN(x,0)-1 DO
  371. xx := (x[k] - x0)*idx;
  372. IF ODD(degree) THEN j := ENTIER(xx) - (degree DIV 2);
  373. ELSE j := ENTIER(xx+0.5D0) - (degree DIV 2);
  374. END;
  375. FOR i := 0 TO degree DO ind0[i] := j+i; END;
  376. CASE degree OF
  377. |1:
  378. s := xx - ind0[0];
  379. w0[0] := 1.0D0-s;
  380. w0[1] := s;
  381. |2:
  382. s := xx - ind0[1];
  383. w0[1] := 3.0D0 / 4.0D0 - s * s;
  384. w0[2] := (1.0D0 / 2.0D0) * (s - w0[1] + 1.0D0);
  385. w0[0] := 1.0D0 - w0[1] - w0[2];
  386. |3:
  387. s := xx - ind0[1];
  388. w0[3] := (1.0D0 / 6.0D0) * s * s * s;
  389. w0[0] := (1.0D0 / 6.0D0) + (1.0D0 / 2.0D0) * s * (s - 1.0D0) - w0[3];
  390. w0[2] := s + w0[0] - 2.0D0 * w0[3];
  391. w0[1] := 1.0D0 - w0[0] - w0[2] - w0[3];
  392. |4:
  393. s := xx - ind0[2];
  394. s2 := s * s;
  395. t := (1.0D0 / 6.0D0) * s2;
  396. w0[0] := 1.0D0 / 2.0D0 - s;
  397. w0[0] := w0[0] * w0[0];
  398. w0[0] := w0[0] * (1.0D0 / 24.0D0) * w0[0];
  399. t0 := s * (t - 11.0D0 / 24.0D0);
  400. t1 := 19.0D0 / 96.0D0 + s2 * (1.0D0 / 4.0D0 - t);
  401. w0[1] := t1 + t0;
  402. w0[3] := t1 - t0;
  403. w0[4] := w0[0] + t0 + (1.0D0 / 2.0D0) * s;
  404. w0[2] := 1.0D0 - w0[0] - w0[1] - w0[3] - w0[4];
  405. |5:
  406. s := xx - ind0[2];
  407. s2 := s * s;
  408. w0[5] := (1.0D0 / 120.0D0) * s * s2 * s2;
  409. s2 := s2 - s;
  410. s4 := s2 * s2;
  411. s := s - 1.0D0 / 2.0D0;
  412. t := s2 * (s2 - 3.0D0);
  413. w0[0] := (1.0D0 / 24.0D0) * (1.0D0 / 5.0D0 + s2 + s4) - w0[5];
  414. t0 := (1.0D0 / 24.0D0) * (s2 * (s2 - 5.0D0) + 46.0D0 / 5.0D0);
  415. t1 := (-1.0D0 / 12.0D0) * s * (t + 4.0D0);
  416. w0[2] := t0 + t1;
  417. w0[3] := t0 - t1;
  418. t0 := (1.0D0 / 16.0D0) * (9.0D0 / 5.0D0 - t);
  419. t1 := (1.0D0 / 24.0D0) * s * (s4 - s2 - 5.0D0);
  420. w0[1] := t0 + t1;
  421. w0[4] := t0 - t1;
  422. |6:
  423. s := xx - ind0[3];
  424. w0[0] := 1.0D0 / 2.0D0 - s;
  425. w0[0] := w0[0] * w0[0] * w0[0];
  426. w0[0] := w0[0] * w0[0] / 720.0D0;
  427. w0[1] := (361.0D0 / 192.0D0 - s * (59.0D0 / 8.0D0 + s * (-185.0D0 / 16.0D0 + s * (25.0D0 / 3.0D0 + s * (-5.0D0 / 2.0D0 + s) * (1.0D0 / 2.0D0 + s))))) / 120.0D0;
  428. w0[2] := (10543.0D0 / 960.0D0 + s * (-289.0D0 / 16.0D0 + s * (79.0D0 / 16.0D0 + s * (43.0D0 / 6.0D0 + s * (-17.0D0 / 4.0D0 + s * (-1.0D0 + s)))))) / 48.0D0;
  429. s2 := s * s;
  430. w0[3] := (5887.0D0 / 320.0D0 - s2 * (231.0D0 / 16.0D0 - s2 * (21.0D0 / 4.0D0 - s2))) / 36.0D0;
  431. w0[4] := (10543.0D0 / 960.0D0 + s * (289.0D0 / 16.0D0 + s * (79.0D0 / 16.0D0 + s * (-43.0D0 / 6.0D0 + s * (-17.0D0 / 4.0D0 + s * (1.0D0 + s)))))) / 48.0D0;
  432. w0[6] := 1.0D0 / 2.0D0 + s;
  433. w0[6] := w0[6] * w0[6] * w0[6];
  434. w0[6] := w0[6] * w0[6] / 720.0D0;
  435. w0[5] := 1.0D0 - w0[0] - w0[1] - w0[2] - w0[3] - w0[4] - w0[6];
  436. |7:
  437. s := xx - ind0[3];
  438. w0[0] := 1.0D0 - s;
  439. w0[0] := w0[0] * w0[0];
  440. w0[0] := w0[0] * w0[0] * w0[0];
  441. w0[0] := w0[0] * (1.0D0 - s) / 5040.0D0;
  442. s2 := s * s;
  443. w0[1] := (120.0D0 / 7.0D0 + s * (-56.0D0 + s * (72.0D0 + s * (-40.0D0 + s2 * (12.0D0 + s * (-6.0D0 + s)))))) / 720.0D0;
  444. w0[2] := (397.0D0 / 7.0D0 - s * (245.0D0 / 3.0D0 + s * (-15.0D0 + s * (-95.0D0 / 3.0D0 + s * (15.0D0 + s * (5.0D0 + s * (-5.0D0 + s))))))) / 240.0D0;
  445. w0[3] := (2416.0D0 / 35.0D0 + s2 * (-48.0D0 + s2 * (16.0D0 + s2 * (-4.0D0 + s)))) / 144.0D0;
  446. w0[4] := (1191.0D0 / 35.0D0 - s * (-49.0D0 + s * (-9.0D0 + s * (19.0D0 + s * (-3.0D0 + s) * (-3.0D0 + s2))))) / 144.0D0;
  447. w0[5] := (40.0D0 / 7.0D0 + s * (56.0D0 / 3.0D0 + s * (24.0D0 + s * (40.0D0 / 3.0D0 + s2 * (-4.0D0 + s * (-2.0D0 + s)))))) / 240.0D0;
  448. w0[7] := s2;
  449. w0[7] := w0[7] * w0[7] * w0[7];
  450. w0[7] := w0[7] * s / 5040.0D0;
  451. w0[6] := 1.0D0 - w0[0] - w0[1] - w0[2] - w0[3] - w0[4] - w0[5] - w0[7];
  452. |8:
  453. s := xx - ind0[4];
  454. w0[0] := 1.0D0 / 2.0D0 - s;
  455. w0[0] := w0[0] * w0[0];
  456. w0[0] := w0[0] * w0[0];
  457. w0[0] := w0[0] * w0[0] / 40320.0D0;
  458. s2 := s * s;
  459. w0[1] := (39.0D0 / 16.0D0 - s * (6.0D0 + s * (-9.0D0 / 2.0D0 + s2))) * (21.0D0 / 16.0D0 + s * (-15.0D0 / 4.0D0 + s * (9.0D0 / 2.0D0 + s * (-3.0D0 + s)))) / 5040.0D0;
  460. w0[2] := (82903.0D0 / 1792.0D0 + s * (-4177.0D0 / 32.0D0 + s * (2275.0D0 / 16.0D0 + s * (-487.0D0 / 8.0D0 + s * (-85.0D0 / 8.0D0 + s * (41.0D0 / 2.0D0 + s * (-5.0D0 + s * (-2.0D0 + s)))))))) / 1440.0D0;
  461. w0[3] := (310661.0D0 / 1792.0D0 - s * (14219.0D0 / 64.0D0 + s * (-199.0D0 / 8.0D0 + s * (-1327.0D0 / 16.0D0 + s * (245.0D0 / 8.0D0 + s * (53.0D0 / 4.0D0 + s * (-8.0D0 + s * (-1.0D0 + s)))))))) / 720.0D0;
  462. w0[4] := (2337507.0D0 / 8960.0D0 + s2 * (-2601.0D0 / 16.0D0 + s2 * (387.0D0 / 8.0D0 + s2 * (-9.0D0 + s2)))) / 576.0D0;
  463. w0[5] := (310661.0D0 / 1792.0D0 - s * (-14219.0D0 / 64.0D0 + s * (-199.0D0 / 8.0D0 + s * (1327.0D0 / 16.0D0 + s * (245.0D0 / 8.0D0 + s * (-53.0D0 / 4.0D0 + s * (-8.0D0 + s * (1.0D0 + s)))))))) / 720.0D0;
  464. w0[7] := (39.0D0 / 16.0D0 - s * (-6.0D0 + s * (-9.0D0 / 2.0D0 + s2))) * (21.0D0 / 16.0D0 + s * (15.0D0 / 4.0D0 + s * (9.0D0 / 2.0D0 + s * (3.0D0 + s)))) / 5040.0D0;
  465. w0[8] := 1.0D0 / 2.0D0 + s;
  466. w0[8] := w0[8] * w0[8];
  467. w0[8] := w0[8] * w0[8];
  468. w0[8] := w0[8] * w0[8] / 40320.0D0;
  469. w0[6] := 1.0D0 - w0[0] - w0[1] - w0[2] - w0[3] - w0[4] - w0[5] - w0[7] - w0[8];
  470. |9:
  471. s := xx - ind0[4];
  472. w0[0] := 1.0D0 - s;
  473. w0[0] := w0[0] * w0[0];
  474. w0[0] := w0[0] * w0[0];
  475. w0[0] := w0[0] * w0[0] * (1.0D0 - s) / 362880.0D0;
  476. w0[1] := (502.0D0 / 9.0D0 + s * (-246.0D0 + s * (472.0D0 + s * (-504.0D0 + s * (308.0D0 + s * (-84.0D0 + s * (-56.0D0 / 3.0D0 + s * (24.0D0 + s * (-8.0D0 + s))))))))) / 40320.0D0;
  477. w0[2] := (3652.0D0 / 9.0D0 - s * (2023.0D0 / 2.0D0 + s * (-952.0D0 + s * (938.0D0 / 3.0D0 + s * (112.0D0 + s * (-119.0D0 + s * (56.0D0 / 3.0D0 + s * (14.0D0 + s * (-7.0D0 + s))))))))) / 10080.0D0;
  478. w0[3] := (44117.0D0 / 42.0D0 + s * (-2427.0D0 / 2.0D0 + s * (66.0D0 + s * (434.0D0 + s * (-129.0D0 + s * (-69.0D0 + s * (34.0D0 + s * (6.0D0 + s * (-6.0D0 + s))))))))) / 4320.0D0;
  479. s2 := s * s;
  480. w0[4] := (78095.0D0 / 63.0D0 - s2 * (700.0D0 + s2 * (-190.0D0 + s2 * (100.0D0 / 3.0D0 + s2 * (-5.0D0 + s))))) / 2880.0D0;
  481. w0[5] := (44117.0D0 / 63.0D0 + s * (809.0D0 + s * (44.0D0 + s * (-868.0D0 / 3.0D0 + s * (-86.0D0 + s * (46.0D0 + s * (68.0D0 / 3.0D0 + s * (-4.0D0 + s * (-4.0D0 + s))))))))) / 2880.0D0;
  482. w0[6] := (3652.0D0 / 21.0D0 - s * (-867.0D0 / 2.0D0 + s * (-408.0D0 + s * (-134.0D0 + s * (48.0D0 + s * (51.0D0 + s * (-4.0D0 + s) * (-1.0D0 + s) * (2.0D0 + s))))))) / 4320.0D0;
  483. w0[7] := (251.0D0 / 18.0D0 + s * (123.0D0 / 2.0D0 + s * (118.0D0 + s * (126.0D0 + s * (77.0D0 + s * (21.0D0 + s * (-14.0D0 / 3.0D0 + s * (-6.0D0 + s * (-2.0D0 + s))))))))) / 10080.0D0;
  484. w0[9] := s2 * s2;
  485. w0[9] := w0[9] * w0[9] * s / 362880.0D0;
  486. w0[8] := 1.0D0 - w0[0] - w0[1] - w0[2] - w0[3] - w0[4] - w0[5] - w0[6] - w0[7] - w0[9];
  487. ELSE
  488. KernelLog.String("Unsupported B-spline degree!"); KernelLog.Ln;
  489. HALT(100);
  490. END;
  491. CASE boundary OF
  492. |MirrorW:
  493. n := 2*LEN(c,0)-2;
  494. FOR i := 0 TO degree DO
  495. IF (ind0[i] < 0) OR (ind0[i] >= LEN(c,0)) THEN
  496. ind0[i] := ABS(ind0[i]) MOD n;
  497. IF ind0[i] >= LEN(c,0) THEN ind0[i] := n - ind0[i]; END;
  498. END;
  499. END;
  500. |Periodic:
  501. n := LEN(c,0);
  502. FOR i := 0 TO degree DO
  503. IF ind0[i] < 0 THEN INC(ind0[i],((n - ind0[i] - 1) DIV n)*n);
  504. ELSIF ind0[i] >= n THEN ind0[i] := ind0[i] MOD n;
  505. END;
  506. END;
  507. ELSE
  508. HALT(100);
  509. END;
  510. s := 0;
  511. FOR i := 0 TO degree DO
  512. s := s + w0[i]*c[ind0[i]];
  513. END;
  514. v[k] := s;
  515. END;
  516. END Interpolate;
  517. (*
  518. Integer power: y := x^p
  519. *)
  520. PROCEDURE IPower(x: LONGREAL; p: LONGINT): LONGREAL;
  521. BEGIN
  522. IF x > 0 THEN
  523. RETURN MathL.exp(p*MathL.ln(x));
  524. ELSIF x # 0 THEN
  525. IF ODD(p) THEN
  526. RETURN -MathL.exp(p*MathL.ln(-x));
  527. ELSE
  528. RETURN MathL.exp(p*MathL.ln(-x));
  529. END;
  530. ELSE
  531. RETURN 0;
  532. END;
  533. END IPower;
  534. END BSplineCurves.