Stage.Spline.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. //
  2. // The graphics engine GLScene
  3. //
  4. unit Stage.Spline;
  5. (* Cubic spline interpolation functions *)
  6. interface
  7. uses
  8. Stage.VectorTypes,
  9. Stage.VectorGeometry;
  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): TGLVector; overload;
  53. // Calculates vector at time t.
  54. procedure SplineVector(const t: Single; var vector: TGLVector); 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. implementation // ------------------------------------------------------------
  76. procedure VECCholeskyTriDiagResol(const b: array of Single; const nb: Integer;
  77. var Result: array of Single);
  78. var
  79. Y, LDiag, LssDiag: array of Single;
  80. i, k, Debut, Fin: Integer;
  81. begin
  82. Debut := 0;
  83. Fin := nb - 1;
  84. Assert(Length(b) > 0);
  85. SetLength(LDiag, nb);
  86. SetLength(LssDiag, nb - 1);
  87. LDiag[Debut] := 1.4142135; // = sqrt(2)
  88. LssDiag[Debut] := 1.0 / 1.4142135;
  89. for k := Debut + 1 to Fin - 1 do
  90. begin
  91. LDiag[k] := Sqrt(4 - LssDiag[k - 1] * LssDiag[k - 1]);
  92. LssDiag[k] := 1.0 / LDiag[k];
  93. end;
  94. LDiag[Fin] := Sqrt(2 - LssDiag[Fin - 1] * LssDiag[Fin - 1]);
  95. SetLength(Y, nb);
  96. Y[Debut] := b[Debut] / LDiag[Debut];
  97. for i := Debut + 1 to Fin do
  98. Y[i] := (b[i] - Y[i - 1] * LssDiag[i - 1]) / LDiag[i];
  99. Assert(Length(Result) = nb);
  100. Result[Fin] := Y[Fin] / LDiag[Fin];
  101. for i := Fin - 1 downto Debut do
  102. Result[i] := (Y[i] - Result[i + 1] * LssDiag[i]) / LDiag[i];
  103. end;
  104. procedure MATInterpolationHermite(const ordonnees: PFloatArray;
  105. const nb: Integer; var Result: TCubicSplineMatrix); {$IFDEF CLR}unsafe;
  106. {$ENDIF}
  107. var
  108. a, b, c, d: Single;
  109. i, n: Integer;
  110. bb, deriv: array of Single;
  111. begin
  112. Result := nil;
  113. if Assigned(ordonnees) and (nb > 0) then
  114. begin
  115. n := nb - 1;
  116. SetLength(bb, nb);
  117. bb[0] := 3 * (ordonnees[1] - ordonnees[0]);
  118. bb[n] := 3 * (ordonnees[n] - ordonnees[n - 1]);
  119. for i := 1 to n - 1 do
  120. bb[i] := 3 * (ordonnees[i + 1] - ordonnees[i - 1]);
  121. SetLength(deriv, nb);
  122. VECCholeskyTriDiagResol(bb, nb, deriv);
  123. SetLength(Result, n);
  124. for i := 0 to n - 1 do
  125. begin
  126. a := ordonnees[i];
  127. b := deriv[i];
  128. c := 3 * (ordonnees[i + 1] - ordonnees[i]) - 2 * deriv[i] - deriv[i + 1];
  129. d := -2 * (ordonnees[i + 1] - ordonnees[i]) + deriv[i] + deriv[i + 1];
  130. Result[i][3] := a + i * (i * (c - i * d) - b);
  131. Result[i][2] := b + i * (3 * i * d - 2 * c);
  132. Result[i][1] := c - 3 * i * d;
  133. Result[i][0] := d;
  134. end;
  135. end;
  136. end;
  137. function MATValeurSpline(const spline: TCubicSplineMatrix; const X: Single;
  138. const nb: Integer): Single;
  139. var
  140. i: Integer;
  141. begin
  142. if Length(spline) > 0 then
  143. begin
  144. if X <= 0 then
  145. i := 0
  146. else if X > nb - 1 then
  147. i := nb - 1
  148. else
  149. i := Integer(Trunc(X));
  150. // TODO : the following line looks like a bug...
  151. if i = (nb - 1) then
  152. Dec(i);
  153. Result := ((spline[i][0] * X + spline[i][1]) * X + spline[i][2]) * X +
  154. spline[i][3];
  155. end
  156. else
  157. Result := 0;
  158. end;
  159. function MATValeurSplineSlope(const spline: TCubicSplineMatrix; const X: Single;
  160. const nb: Integer): Single;
  161. var
  162. i: Integer;
  163. begin
  164. if Length(spline) > 0 then
  165. begin
  166. if X <= 0 then
  167. i := 0
  168. else if X > nb - 1 then
  169. i := nb - 1
  170. else
  171. i := Integer(Trunc(X));
  172. // TODO : the following line looks like a bug...
  173. if i = (nb - 1) then
  174. Dec(i);
  175. Result := (3 * spline[i][0] * X + 2 * spline[i][1]) * X + spline[i][2];
  176. end
  177. else
  178. Result := 0;
  179. end;
  180. // ------------------
  181. // ------------------ TCubicSpline ------------------
  182. // ------------------
  183. constructor TCubicSpline.Create(const X, Y, Z, W: PFloatArray;
  184. const nb: Integer); {$IFDEF CLR}unsafe; {$ENDIF}
  185. begin
  186. inherited Create;
  187. MATInterpolationHermite(X, nb, matX);
  188. MATInterpolationHermite(Y, nb, matY);
  189. MATInterpolationHermite(Z, nb, matZ);
  190. MATInterpolationHermite(W, nb, matW);
  191. FNb := nb;
  192. end;
  193. destructor TCubicSpline.Destroy;
  194. begin
  195. inherited Destroy;
  196. end;
  197. function TCubicSpline.SplineX(const t: Single): Single;
  198. begin
  199. Result := MATValeurSpline(matX, t, FNb);
  200. end;
  201. function TCubicSpline.SplineY(const t: Single): Single;
  202. begin
  203. Result := MATValeurSpline(matY, t, FNb);
  204. end;
  205. function TCubicSpline.SplineZ(const t: Single): Single;
  206. begin
  207. Result := MATValeurSpline(matZ, t, FNb);
  208. end;
  209. function TCubicSpline.SplineW(const t: Single): Single;
  210. begin
  211. Result := MATValeurSpline(matW, t, FNb);
  212. end;
  213. procedure TCubicSpline.SplineXY(const t: Single; out X, Y: Single);
  214. begin
  215. X := MATValeurSpline(matX, t, FNb);
  216. Y := MATValeurSpline(matY, t, FNb);
  217. end;
  218. procedure TCubicSpline.SplineXYZ(const t: Single; out X, Y, Z: Single);
  219. begin
  220. X := MATValeurSpline(matX, t, FNb);
  221. Y := MATValeurSpline(matY, t, FNb);
  222. Z := MATValeurSpline(matZ, t, FNb);
  223. end;
  224. procedure TCubicSpline.SplineXYZW(const t: Single; out X, Y, Z, W: Single);
  225. begin
  226. X := MATValeurSpline(matX, t, FNb);
  227. Y := MATValeurSpline(matY, t, FNb);
  228. Z := MATValeurSpline(matZ, t, FNb);
  229. W := MATValeurSpline(matW, t, FNb);
  230. end;
  231. function TCubicSpline.SplineAffineVector(const t: Single): TAffineVector;
  232. begin
  233. Result.X := MATValeurSpline(matX, t, FNb);
  234. Result.Y := MATValeurSpline(matY, t, FNb);
  235. Result.Z := MATValeurSpline(matZ, t, FNb);
  236. end;
  237. procedure TCubicSpline.SplineAffineVector(const t: Single;
  238. var vector: TAffineVector);
  239. begin
  240. vector.X := MATValeurSpline(matX, t, FNb);
  241. vector.Y := MATValeurSpline(matY, t, FNb);
  242. vector.Z := MATValeurSpline(matZ, t, FNb);
  243. end;
  244. function TCubicSpline.SplineVector(const t: Single): TGLVector;
  245. begin
  246. Result.X := MATValeurSpline(matX, t, FNb);
  247. Result.Y := MATValeurSpline(matY, t, FNb);
  248. Result.Z := MATValeurSpline(matZ, t, FNb);
  249. Result.W := MATValeurSpline(matW, t, FNb);
  250. end;
  251. procedure TCubicSpline.SplineVector(const t: Single; var vector: TGLVector);
  252. begin
  253. vector.X := MATValeurSpline(matX, t, FNb);
  254. vector.Y := MATValeurSpline(matY, t, FNb);
  255. vector.Z := MATValeurSpline(matZ, t, FNb);
  256. vector.W := MATValeurSpline(matW, t, FNb);
  257. end;
  258. function TCubicSpline.SplineSlopeX(const t: Single): Single;
  259. begin
  260. Result := MATValeurSplineSlope(matX, t, FNb);
  261. end;
  262. function TCubicSpline.SplineSlopeY(const t: Single): Single;
  263. begin
  264. Result := MATValeurSplineSlope(matY, t, FNb);
  265. end;
  266. function TCubicSpline.SplineSlopeZ(const t: Single): Single;
  267. begin
  268. Result := MATValeurSplineSlope(matZ, t, FNb);
  269. end;
  270. function TCubicSpline.SplineSlopeW(const t: Single): Single;
  271. begin
  272. Result := MATValeurSplineSlope(matW, t, FNb);
  273. end;
  274. function TCubicSpline.SplineSlopeVector(const t: Single): TAffineVector;
  275. begin
  276. Result.X := MATValeurSplineSlope(matX, t, FNb);
  277. Result.Y := MATValeurSplineSlope(matY, t, FNb);
  278. Result.Z := MATValeurSplineSlope(matZ, t, FNb);
  279. end;
  280. function TCubicSpline.SplineIntersecYZ(X: Single; var Y, Z: Single): Boolean;
  281. var
  282. Sup, Inf, Mid: Single;
  283. SSup, Sinf, Smid: Single;
  284. begin
  285. Result := False;
  286. Sup := FNb;
  287. Inf := 0.0;
  288. SSup := SplineX(Sup);
  289. Sinf := SplineX(Inf);
  290. if SSup > Sinf then
  291. begin
  292. if (SSup < X) or (Sinf > X) then
  293. Exit;
  294. while Abs(SSup - Sinf) > 1E-4 do
  295. begin
  296. Mid := (Sup + Inf) * 0.5;
  297. Smid := SplineX(Mid);
  298. if X < Smid then
  299. begin
  300. SSup := Smid;
  301. Sup := Mid;
  302. end
  303. else
  304. begin
  305. Sinf := Smid;
  306. Inf := Mid;
  307. end;
  308. end;
  309. Y := SplineY((Sup + Inf) * 0.5);
  310. Z := SplineZ((Sup + Inf) * 0.5);
  311. end
  312. else
  313. begin
  314. if (Sinf < X) or (SSup > X) then
  315. Exit;
  316. while Abs(SSup - Sinf) > 1E-4 do
  317. begin
  318. Mid := (Sup + Inf) * 0.5;
  319. Smid := SplineX(Mid);
  320. if X < Smid then
  321. begin
  322. Sinf := Smid;
  323. Inf := Mid;
  324. end
  325. else
  326. begin
  327. SSup := Smid;
  328. Sup := Mid;
  329. end;
  330. end;
  331. Y := SplineY((Sup + Inf) * 0.5);
  332. Z := SplineZ((Sup + Inf) * 0.5);
  333. end;
  334. Result := True;
  335. end;
  336. function TCubicSpline.SplineIntersecXZ(Y: Single; var X, Z: Single): Boolean;
  337. var
  338. Sup, Inf, Mid: Single;
  339. SSup, Sinf, Smid: Single;
  340. begin
  341. Result := False;
  342. Sup := FNb;
  343. Inf := 0.0;
  344. SSup := SplineY(Sup);
  345. Sinf := SplineY(Inf);
  346. if SSup > Sinf then
  347. begin
  348. if (SSup < Y) or (Sinf > Y) then
  349. Exit;
  350. while Abs(SSup - Sinf) > 1E-4 do
  351. begin
  352. Mid := (Sup + Inf) * 0.5;
  353. Smid := SplineY(Mid);
  354. if Y < Smid then
  355. begin
  356. SSup := Smid;
  357. Sup := Mid;
  358. end
  359. else
  360. begin
  361. Sinf := Smid;
  362. Inf := Mid;
  363. end;
  364. end;
  365. X := SplineX((Sup + Inf) * 0.5);
  366. Z := SplineZ((Sup + Inf) * 0.5);
  367. end
  368. else
  369. begin
  370. if (Sinf < Y) or (SSup > Y) then
  371. Exit;
  372. while Abs(SSup - Sinf) > 1E-4 do
  373. begin
  374. Mid := (Sup + Inf) * 0.5;
  375. Smid := SplineY(Mid);
  376. if Y < Smid then
  377. begin
  378. Sinf := Smid;
  379. Inf := Mid;
  380. end
  381. else
  382. begin
  383. SSup := Smid;
  384. Sup := Mid;
  385. end;
  386. end;
  387. X := SplineX((Sup + Inf) * 0.5);
  388. Z := SplineZ((Sup + Inf) * 0.5);
  389. end;
  390. Result := True;
  391. end;
  392. function TCubicSpline.SplineIntersecXY(Z: Single; var X, Y: Single): Boolean;
  393. var
  394. Sup, Inf, Mid: Single;
  395. SSup, Sinf, Smid: Single;
  396. begin
  397. Result := False;
  398. Sup := FNb;
  399. Inf := 0.0;
  400. SSup := SplineZ(Sup);
  401. Sinf := SplineZ(Inf);
  402. if SSup > Sinf then
  403. begin
  404. if (SSup < Z) or (Sinf > Z) then
  405. Exit;
  406. while Abs(SSup - Sinf) > 1E-4 do
  407. begin
  408. Mid := (Sup + Inf) * 0.5;
  409. Smid := SplineZ(Mid);
  410. if Z < Smid then
  411. begin
  412. SSup := Smid;
  413. Sup := Mid;
  414. end
  415. else
  416. begin
  417. Sinf := Smid;
  418. Inf := Mid;
  419. end;
  420. end;
  421. X := SplineX((Sup + Inf) * 0.5);
  422. Y := SplineY((Sup + Inf) * 0.5);
  423. end
  424. else
  425. begin
  426. if (Sinf < Z) or (SSup > Z) then
  427. Exit;
  428. while Abs(SSup - Sinf) > 1E-4 do
  429. begin
  430. Mid := (Sup + Inf) * 0.5;
  431. Smid := SplineZ(Mid);
  432. if Z < Smid then
  433. begin
  434. Sinf := Smid;
  435. Inf := Mid;
  436. end
  437. else
  438. begin
  439. SSup := Smid;
  440. Sup := Mid;
  441. end;
  442. end;
  443. X := SplineX((Sup + Inf) * 0.5);
  444. Y := SplineY((Sup + Inf) * 0.5);
  445. end;
  446. Result := True;
  447. end;
  448. //--------------------------------------------------------------------------
  449. end.