GLSpline.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. //
  2. // This unit is part of the GLScene Engine, http://glscene.org
  3. //
  4. unit GLSpline;
  5. (* Cubic spline interpolation functions *)
  6. interface
  7. uses
  8. GLVectorGeometry;
  9. {$I GLScene.inc}
  10. type
  11. TCubicSplineMatrix = array of array [0 .. 3] of Single;
  12. (* 3D cubic spline handler class.
  13. This class allows to describe and calculate values of a time-based,
  14. three-dimensionnal cubic spline.
  15. Cubic spline pass through all given points and tangent on point N is
  16. given by the (N-1) to (N+1) vector.
  17. Note : X, Y & Z are actually interpolated independently. *)
  18. TCubicSpline = class(TObject)
  19. private
  20. matX, matY, matZ, matW: TCubicSplineMatrix;
  21. FNb: Integer;
  22. public
  23. (* Creates the spline and declares interpolation points.
  24. Time references go from 0 (first point) to nb-1 (last point), the
  25. first and last reference matrices respectively are used when T is
  26. used beyond this range.
  27. Note : "nil" single arrays are accepted, in this case the axis is
  28. disabled and calculus will return 0 (zero) for this component. *)
  29. constructor Create(const X, Y, Z, W: PFloatArray; const nb: Integer);
  30. {$IFDEF CLR} unsafe; {$ENDIF}
  31. destructor Destroy; override;
  32. // Calculates X component at time t.
  33. function SplineX(const t: Single): Single;
  34. // Calculates Y component at time t.
  35. function SplineY(const t: Single): Single;
  36. // Calculates Z component at time t.
  37. function SplineZ(const t: Single): Single;
  38. // Calculates W component at time t.
  39. function SplineW(const t: Single): Single;
  40. // Calculates X and Y components at time t.
  41. procedure SplineXY(const t: Single; out X, Y: Single);
  42. // Calculates X, Y and Z components at time t.
  43. procedure SplineXYZ(const t: Single; out X, Y, Z: Single);
  44. // Calculates X, Y, Z and W components at time t.
  45. procedure SplineXYZW(const t: Single; out X, Y, Z, W: Single);
  46. // Calculates affine vector at time t.
  47. function SplineAffineVector(const t: Single): TAffineVector; overload;
  48. // Calculates affine vector at time t.
  49. procedure SplineAffineVector(const t: Single;
  50. var vector: TAffineVector); overload;
  51. // Calculates vector at time t.
  52. function SplineVector(const t: Single): TVector; overload;
  53. // Calculates vector at time t.
  54. procedure SplineVector(const t: Single; var vector: TVector); overload;
  55. // Calculates X component slope at time t.
  56. function SplineSlopeX(const t: Single): Single;
  57. // Calculates Y component slope at time t.
  58. function SplineSlopeY(const t: Single): Single;
  59. // Calculates Z component slope at time t.
  60. function SplineSlopeZ(const t: Single): Single;
  61. // Calculates W component slope at time t.
  62. function SplineSlopeW(const t: Single): Single;
  63. // Calculates the spline slope at time t.
  64. function SplineSlopeVector(const t: Single): TAffineVector; overload;
  65. (* Calculates the intersection of the spline with the YZ plane.
  66. Returns True if an intersection was found. *)
  67. function SplineIntersecYZ(X: Single; var Y, Z: Single): Boolean;
  68. (* Calculates the intersection of the spline with the XZ plane.
  69. Returns True if an intersection was found. *)
  70. function SplineIntersecXZ(Y: Single; var X, Z: Single): Boolean;
  71. (* Calculates the intersection of the spline with the XY plane.
  72. Returns True if an intersection was found. *)
  73. function SplineIntersecXY(Z: Single; var X, Y: Single): Boolean;
  74. end;
  75. // ------------------------------------------------------------------
  76. implementation
  77. // ------------------------------------------------------------------
  78. procedure VECCholeskyTriDiagResol(const b: array of Single; const nb: Integer;
  79. var Result: array of Single);
  80. var
  81. Y, LDiag, LssDiag: array of Single;
  82. i, k, Debut, Fin: Integer;
  83. begin
  84. Debut := 0;
  85. Fin := nb - 1;
  86. Assert(Length(b) > 0);
  87. SetLength(LDiag, nb);
  88. SetLength(LssDiag, nb - 1);
  89. LDiag[Debut] := 1.4142135; // = sqrt(2)
  90. LssDiag[Debut] := 1.0 / 1.4142135;
  91. for k := Debut + 1 to Fin - 1 do
  92. begin
  93. LDiag[k] := Sqrt(4 - LssDiag[k - 1] * LssDiag[k - 1]);
  94. LssDiag[k] := 1.0 / LDiag[k];
  95. end;
  96. LDiag[Fin] := Sqrt(2 - LssDiag[Fin - 1] * LssDiag[Fin - 1]);
  97. SetLength(Y, nb);
  98. Y[Debut] := b[Debut] / LDiag[Debut];
  99. for i := Debut + 1 to Fin do
  100. Y[i] := (b[i] - Y[i - 1] * LssDiag[i - 1]) / LDiag[i];
  101. Assert(Length(Result) = nb);
  102. Result[Fin] := Y[Fin] / LDiag[Fin];
  103. for i := Fin - 1 downto Debut do
  104. Result[i] := (Y[i] - Result[i + 1] * LssDiag[i]) / LDiag[i];
  105. end;
  106. procedure MATInterpolationHermite(const ordonnees: PFloatArray;
  107. const nb: Integer; var Result: TCubicSplineMatrix); {$IFDEF CLR}unsafe;
  108. {$ENDIF}
  109. var
  110. a, b, c, d: Single;
  111. i, n: Integer;
  112. bb, deriv: array of Single;
  113. begin
  114. Result := nil;
  115. if Assigned(ordonnees) and (nb > 0) then
  116. begin
  117. n := nb - 1;
  118. SetLength(bb, nb);
  119. bb[0] := 3 * (ordonnees[1] - ordonnees[0]);
  120. bb[n] := 3 * (ordonnees[n] - ordonnees[n - 1]);
  121. for i := 1 to n - 1 do
  122. bb[i] := 3 * (ordonnees[i + 1] - ordonnees[i - 1]);
  123. SetLength(deriv, nb);
  124. VECCholeskyTriDiagResol(bb, nb, deriv);
  125. SetLength(Result, n);
  126. for i := 0 to n - 1 do
  127. begin
  128. a := ordonnees[i];
  129. b := deriv[i];
  130. c := 3 * (ordonnees[i + 1] - ordonnees[i]) - 2 * deriv[i] - deriv[i + 1];
  131. d := -2 * (ordonnees[i + 1] - ordonnees[i]) + deriv[i] + deriv[i + 1];
  132. Result[i][3] := a + i * (i * (c - i * d) - b);
  133. Result[i][2] := b + i * (3 * i * d - 2 * c);
  134. Result[i][1] := c - 3 * i * d;
  135. Result[i][0] := d;
  136. end;
  137. end;
  138. end;
  139. function MATValeurSpline(const spline: TCubicSplineMatrix; const X: Single;
  140. const nb: Integer): Single;
  141. var
  142. i: Integer;
  143. begin
  144. if Length(spline) > 0 then
  145. begin
  146. if X <= 0 then
  147. i := 0
  148. else if X > nb - 1 then
  149. i := nb - 1
  150. else
  151. i := Integer(Trunc(X));
  152. { TODO : the following line looks like a bug... }
  153. if i = (nb - 1) then
  154. Dec(i);
  155. Result := ((spline[i][0] * X + spline[i][1]) * X + spline[i][2]) * X +
  156. spline[i][3];
  157. end
  158. else
  159. Result := 0;
  160. end;
  161. function MATValeurSplineSlope(const spline: TCubicSplineMatrix; const X: Single;
  162. const nb: Integer): Single;
  163. var
  164. i: Integer;
  165. begin
  166. if Length(spline) > 0 then
  167. begin
  168. if X <= 0 then
  169. i := 0
  170. else if X > nb - 1 then
  171. i := nb - 1
  172. else
  173. i := Integer(Trunc(X));
  174. { TODO : the following line looks like a bug... }
  175. if i = (nb - 1) then
  176. Dec(i);
  177. Result := (3 * spline[i][0] * X + 2 * spline[i][1]) * X + spline[i][2];
  178. end
  179. else
  180. Result := 0;
  181. end;
  182. // ------------------
  183. // ------------------ TCubicSpline ------------------
  184. // ------------------
  185. constructor TCubicSpline.Create(const X, Y, Z, W: PFloatArray;
  186. const nb: Integer); {$IFDEF CLR}unsafe; {$ENDIF}
  187. begin
  188. inherited Create;
  189. MATInterpolationHermite(X, nb, matX);
  190. MATInterpolationHermite(Y, nb, matY);
  191. MATInterpolationHermite(Z, nb, matZ);
  192. MATInterpolationHermite(W, nb, matW);
  193. FNb := nb;
  194. end;
  195. destructor TCubicSpline.Destroy;
  196. begin
  197. inherited Destroy;
  198. end;
  199. function TCubicSpline.SplineX(const t: Single): Single;
  200. begin
  201. Result := MATValeurSpline(matX, t, FNb);
  202. end;
  203. function TCubicSpline.SplineY(const t: Single): Single;
  204. begin
  205. Result := MATValeurSpline(matY, t, FNb);
  206. end;
  207. function TCubicSpline.SplineZ(const t: Single): Single;
  208. begin
  209. Result := MATValeurSpline(matZ, t, FNb);
  210. end;
  211. function TCubicSpline.SplineW(const t: Single): Single;
  212. begin
  213. Result := MATValeurSpline(matW, t, FNb);
  214. end;
  215. procedure TCubicSpline.SplineXY(const t: Single; out X, Y: Single);
  216. begin
  217. X := MATValeurSpline(matX, t, FNb);
  218. Y := MATValeurSpline(matY, t, FNb);
  219. end;
  220. procedure TCubicSpline.SplineXYZ(const t: Single; out X, Y, Z: Single);
  221. begin
  222. X := MATValeurSpline(matX, t, FNb);
  223. Y := MATValeurSpline(matY, t, FNb);
  224. Z := MATValeurSpline(matZ, t, FNb);
  225. end;
  226. procedure TCubicSpline.SplineXYZW(const t: Single; out X, Y, Z, W: Single);
  227. begin
  228. X := MATValeurSpline(matX, t, FNb);
  229. Y := MATValeurSpline(matY, t, FNb);
  230. Z := MATValeurSpline(matZ, t, FNb);
  231. W := MATValeurSpline(matW, t, FNb);
  232. end;
  233. function TCubicSpline.SplineAffineVector(const t: Single): TAffineVector;
  234. begin
  235. Result.X := MATValeurSpline(matX, t, FNb);
  236. Result.Y := MATValeurSpline(matY, t, FNb);
  237. Result.Z := MATValeurSpline(matZ, t, FNb);
  238. end;
  239. procedure TCubicSpline.SplineAffineVector(const t: Single;
  240. var vector: TAffineVector);
  241. begin
  242. vector.X := MATValeurSpline(matX, t, FNb);
  243. vector.Y := MATValeurSpline(matY, t, FNb);
  244. vector.Z := MATValeurSpline(matZ, t, FNb);
  245. end;
  246. function TCubicSpline.SplineVector(const t: Single): TVector;
  247. begin
  248. Result.X := MATValeurSpline(matX, t, FNb);
  249. Result.Y := MATValeurSpline(matY, t, FNb);
  250. Result.Z := MATValeurSpline(matZ, t, FNb);
  251. Result.W := MATValeurSpline(matW, t, FNb);
  252. end;
  253. procedure TCubicSpline.SplineVector(const t: Single; var vector: TVector);
  254. begin
  255. vector.X := MATValeurSpline(matX, t, FNb);
  256. vector.Y := MATValeurSpline(matY, t, FNb);
  257. vector.Z := MATValeurSpline(matZ, t, FNb);
  258. vector.W := MATValeurSpline(matW, t, FNb);
  259. end;
  260. function TCubicSpline.SplineSlopeX(const t: Single): Single;
  261. begin
  262. Result := MATValeurSplineSlope(matX, t, FNb);
  263. end;
  264. function TCubicSpline.SplineSlopeY(const t: Single): Single;
  265. begin
  266. Result := MATValeurSplineSlope(matY, t, FNb);
  267. end;
  268. function TCubicSpline.SplineSlopeZ(const t: Single): Single;
  269. begin
  270. Result := MATValeurSplineSlope(matZ, t, FNb);
  271. end;
  272. function TCubicSpline.SplineSlopeW(const t: Single): Single;
  273. begin
  274. Result := MATValeurSplineSlope(matW, t, FNb);
  275. end;
  276. function TCubicSpline.SplineSlopeVector(const t: Single): TAffineVector;
  277. begin
  278. Result.X := MATValeurSplineSlope(matX, t, FNb);
  279. Result.Y := MATValeurSplineSlope(matY, t, FNb);
  280. Result.Z := MATValeurSplineSlope(matZ, t, FNb);
  281. end;
  282. function TCubicSpline.SplineIntersecYZ(X: Single; var Y, Z: Single): Boolean;
  283. var
  284. Sup, Inf, Mid: Single;
  285. SSup, Sinf, Smid: Single;
  286. begin
  287. Result := False;
  288. Sup := FNb;
  289. Inf := 0.0;
  290. SSup := SplineX(Sup);
  291. Sinf := SplineX(Inf);
  292. if SSup > Sinf then
  293. begin
  294. if (SSup < X) or (Sinf > X) then
  295. Exit;
  296. while Abs(SSup - Sinf) > 1E-4 do
  297. begin
  298. Mid := (Sup + Inf) * 0.5;
  299. Smid := SplineX(Mid);
  300. if X < Smid then
  301. begin
  302. SSup := Smid;
  303. Sup := Mid;
  304. end
  305. else
  306. begin
  307. Sinf := Smid;
  308. Inf := Mid;
  309. end;
  310. end;
  311. Y := SplineY((Sup + Inf) * 0.5);
  312. Z := SplineZ((Sup + Inf) * 0.5);
  313. end
  314. else
  315. begin
  316. if (Sinf < X) or (SSup > X) then
  317. Exit;
  318. while Abs(SSup - Sinf) > 1E-4 do
  319. begin
  320. Mid := (Sup + Inf) * 0.5;
  321. Smid := SplineX(Mid);
  322. if X < Smid then
  323. begin
  324. Sinf := Smid;
  325. Inf := Mid;
  326. end
  327. else
  328. begin
  329. SSup := Smid;
  330. Sup := Mid;
  331. end;
  332. end;
  333. Y := SplineY((Sup + Inf) * 0.5);
  334. Z := SplineZ((Sup + Inf) * 0.5);
  335. end;
  336. Result := True;
  337. end;
  338. function TCubicSpline.SplineIntersecXZ(Y: Single; var X, Z: Single): Boolean;
  339. var
  340. Sup, Inf, Mid: Single;
  341. SSup, Sinf, Smid: Single;
  342. begin
  343. Result := False;
  344. Sup := FNb;
  345. Inf := 0.0;
  346. SSup := SplineY(Sup);
  347. Sinf := SplineY(Inf);
  348. if SSup > Sinf then
  349. begin
  350. if (SSup < Y) or (Sinf > Y) then
  351. Exit;
  352. while Abs(SSup - Sinf) > 1E-4 do
  353. begin
  354. Mid := (Sup + Inf) * 0.5;
  355. Smid := SplineY(Mid);
  356. if Y < Smid then
  357. begin
  358. SSup := Smid;
  359. Sup := Mid;
  360. end
  361. else
  362. begin
  363. Sinf := Smid;
  364. Inf := Mid;
  365. end;
  366. end;
  367. X := SplineX((Sup + Inf) * 0.5);
  368. Z := SplineZ((Sup + Inf) * 0.5);
  369. end
  370. else
  371. begin
  372. if (Sinf < Y) or (SSup > Y) then
  373. Exit;
  374. while Abs(SSup - Sinf) > 1E-4 do
  375. begin
  376. Mid := (Sup + Inf) * 0.5;
  377. Smid := SplineY(Mid);
  378. if Y < Smid then
  379. begin
  380. Sinf := Smid;
  381. Inf := Mid;
  382. end
  383. else
  384. begin
  385. SSup := Smid;
  386. Sup := Mid;
  387. end;
  388. end;
  389. X := SplineX((Sup + Inf) * 0.5);
  390. Z := SplineZ((Sup + Inf) * 0.5);
  391. end;
  392. Result := True;
  393. end;
  394. function TCubicSpline.SplineIntersecXY(Z: Single; var X, Y: Single): Boolean;
  395. var
  396. Sup, Inf, Mid: Single;
  397. SSup, Sinf, Smid: Single;
  398. begin
  399. Result := False;
  400. Sup := FNb;
  401. Inf := 0.0;
  402. SSup := SplineZ(Sup);
  403. Sinf := SplineZ(Inf);
  404. if SSup > Sinf then
  405. begin
  406. if (SSup < Z) or (Sinf > Z) then
  407. Exit;
  408. while Abs(SSup - Sinf) > 1E-4 do
  409. begin
  410. Mid := (Sup + Inf) * 0.5;
  411. Smid := SplineZ(Mid);
  412. if Z < Smid then
  413. begin
  414. SSup := Smid;
  415. Sup := Mid;
  416. end
  417. else
  418. begin
  419. Sinf := Smid;
  420. Inf := Mid;
  421. end;
  422. end;
  423. X := SplineX((Sup + Inf) * 0.5);
  424. Y := SplineY((Sup + Inf) * 0.5);
  425. end
  426. else
  427. begin
  428. if (Sinf < Z) or (SSup > Z) then
  429. Exit;
  430. while Abs(SSup - Sinf) > 1E-4 do
  431. begin
  432. Mid := (Sup + Inf) * 0.5;
  433. Smid := SplineZ(Mid);
  434. if Z < Smid then
  435. begin
  436. Sinf := Smid;
  437. Inf := Mid;
  438. end
  439. else
  440. begin
  441. SSup := Smid;
  442. Sup := Mid;
  443. end;
  444. end;
  445. X := SplineX((Sup + Inf) * 0.5);
  446. Y := SplineY((Sup + Inf) * 0.5);
  447. end;
  448. Result := True;
  449. end;
  450. end.