123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490 |
- //
- // The graphics engine GLXEngine
- //
- unit Stage.Spline;
- (* Cubic spline interpolation functions *)
- interface
- uses
- Stage.VectorTypes,
- Stage.VectorGeometry;
- type
- TCubicSplineMatrix = array of array [0 .. 3] of Single;
- (* 3D cubic spline handler class.
- This class allows to describe and calculate values of a time-based,
- three-dimensionnal cubic spline.
- Cubic spline pass through all given points and tangent on point N is
- given by the (N-1) to (N+1) vector.
- Note : X, Y & Z are actually interpolated independently. *)
- TCubicSpline = class(TObject)
- private
- matX, matY, matZ, matW: TCubicSplineMatrix;
- FNb: Integer;
- public
- (* Creates the spline and declares interpolation points.
- Time references go from 0 (first point) to nb-1 (last point), the
- first and last reference matrices respectively are used when T is
- used beyond this range.
- Note : "nil" single arrays are accepted, in this case the axis is
- disabled and calculus will return 0 (zero) for this component. *)
- constructor Create(const X, Y, Z, W: PFloatArray; const nb: Integer);
- {$IFDEF CLR} unsafe; {$ENDIF}
- destructor Destroy; override;
- // Calculates X component at time t.
- function SplineX(const t: Single): Single;
- // Calculates Y component at time t.
- function SplineY(const t: Single): Single;
- // Calculates Z component at time t.
- function SplineZ(const t: Single): Single;
- // Calculates W component at time t.
- function SplineW(const t: Single): Single;
- // Calculates X and Y components at time t.
- procedure SplineXY(const t: Single; out X, Y: Single);
- // Calculates X, Y and Z components at time t.
- procedure SplineXYZ(const t: Single; out X, Y, Z: Single);
- // Calculates X, Y, Z and W components at time t.
- procedure SplineXYZW(const t: Single; out X, Y, Z, W: Single);
- // Calculates affine vector at time t.
- function SplineAffineVector(const t: Single): TAffineVector; overload;
- // Calculates affine vector at time t.
- procedure SplineAffineVector(const t: Single;
- var vector: TAffineVector); overload;
- // Calculates vector at time t.
- function SplineVector(const t: Single): TGLVector; overload;
- // Calculates vector at time t.
- procedure SplineVector(const t: Single; var vector: TGLVector); overload;
- // Calculates X component slope at time t.
- function SplineSlopeX(const t: Single): Single;
- // Calculates Y component slope at time t.
- function SplineSlopeY(const t: Single): Single;
- // Calculates Z component slope at time t.
- function SplineSlopeZ(const t: Single): Single;
- // Calculates W component slope at time t.
- function SplineSlopeW(const t: Single): Single;
- // Calculates the spline slope at time t.
- function SplineSlopeVector(const t: Single): TAffineVector; overload;
- (* Calculates the intersection of the spline with the YZ plane.
- Returns True if an intersection was found. *)
- function SplineIntersecYZ(X: Single; var Y, Z: Single): Boolean;
- (* Calculates the intersection of the spline with the XZ plane.
- Returns True if an intersection was found. *)
- function SplineIntersecXZ(Y: Single; var X, Z: Single): Boolean;
- (* Calculates the intersection of the spline with the XY plane.
- Returns True if an intersection was found. *)
- function SplineIntersecXY(Z: Single; var X, Y: Single): Boolean;
- end;
- implementation // ------------------------------------------------------------
- procedure VECCholeskyTriDiagResol(const b: array of Single; const nb: Integer;
- var Result: array of Single);
- var
- Y, LDiag, LssDiag: array of Single;
- i, k, Debut, Fin: Integer;
- begin
- Debut := 0;
- Fin := nb - 1;
- Assert(Length(b) > 0);
- SetLength(LDiag, nb);
- SetLength(LssDiag, nb - 1);
- LDiag[Debut] := 1.4142135; // = sqrt(2)
- LssDiag[Debut] := 1.0 / 1.4142135;
- for k := Debut + 1 to Fin - 1 do
- begin
- LDiag[k] := Sqrt(4 - LssDiag[k - 1] * LssDiag[k - 1]);
- LssDiag[k] := 1.0 / LDiag[k];
- end;
- LDiag[Fin] := Sqrt(2 - LssDiag[Fin - 1] * LssDiag[Fin - 1]);
- SetLength(Y, nb);
- Y[Debut] := b[Debut] / LDiag[Debut];
- for i := Debut + 1 to Fin do
- Y[i] := (b[i] - Y[i - 1] * LssDiag[i - 1]) / LDiag[i];
- Assert(Length(Result) = nb);
- Result[Fin] := Y[Fin] / LDiag[Fin];
- for i := Fin - 1 downto Debut do
- Result[i] := (Y[i] - Result[i + 1] * LssDiag[i]) / LDiag[i];
- end;
- procedure MATInterpolationHermite(const ordonnees: PFloatArray;
- const nb: Integer; var Result: TCubicSplineMatrix); {$IFDEF CLR}unsafe;
- {$ENDIF}
- var
- a, b, c, d: Single;
- i, n: Integer;
- bb, deriv: array of Single;
- begin
- Result := nil;
- if Assigned(ordonnees) and (nb > 0) then
- begin
- n := nb - 1;
- SetLength(bb, nb);
- bb[0] := 3 * (ordonnees[1] - ordonnees[0]);
- bb[n] := 3 * (ordonnees[n] - ordonnees[n - 1]);
- for i := 1 to n - 1 do
- bb[i] := 3 * (ordonnees[i + 1] - ordonnees[i - 1]);
- SetLength(deriv, nb);
- VECCholeskyTriDiagResol(bb, nb, deriv);
- SetLength(Result, n);
- for i := 0 to n - 1 do
- begin
- a := ordonnees[i];
- b := deriv[i];
- c := 3 * (ordonnees[i + 1] - ordonnees[i]) - 2 * deriv[i] - deriv[i + 1];
- d := -2 * (ordonnees[i + 1] - ordonnees[i]) + deriv[i] + deriv[i + 1];
- Result[i][3] := a + i * (i * (c - i * d) - b);
- Result[i][2] := b + i * (3 * i * d - 2 * c);
- Result[i][1] := c - 3 * i * d;
- Result[i][0] := d;
- end;
- end;
- end;
- function MATValeurSpline(const spline: TCubicSplineMatrix; const X: Single;
- const nb: Integer): Single;
- var
- i: Integer;
- begin
- if Length(spline) > 0 then
- begin
- if X <= 0 then
- i := 0
- else if X > nb - 1 then
- i := nb - 1
- else
- i := Integer(Trunc(X));
- // TODO : the following line looks like a bug...
- if i = (nb - 1) then
- Dec(i);
- Result := ((spline[i][0] * X + spline[i][1]) * X + spline[i][2]) * X +
- spline[i][3];
- end
- else
- Result := 0;
- end;
- function MATValeurSplineSlope(const spline: TCubicSplineMatrix; const X: Single;
- const nb: Integer): Single;
- var
- i: Integer;
- begin
- if Length(spline) > 0 then
- begin
- if X <= 0 then
- i := 0
- else if X > nb - 1 then
- i := nb - 1
- else
- i := Integer(Trunc(X));
- // TODO : the following line looks like a bug...
- if i = (nb - 1) then
- Dec(i);
- Result := (3 * spline[i][0] * X + 2 * spline[i][1]) * X + spline[i][2];
- end
- else
- Result := 0;
- end;
- // ------------------
- // ------------------ TCubicSpline ------------------
- // ------------------
- constructor TCubicSpline.Create(const X, Y, Z, W: PFloatArray;
- const nb: Integer); {$IFDEF CLR}unsafe; {$ENDIF}
- begin
- inherited Create;
- MATInterpolationHermite(X, nb, matX);
- MATInterpolationHermite(Y, nb, matY);
- MATInterpolationHermite(Z, nb, matZ);
- MATInterpolationHermite(W, nb, matW);
- FNb := nb;
- end;
- destructor TCubicSpline.Destroy;
- begin
- inherited Destroy;
- end;
- function TCubicSpline.SplineX(const t: Single): Single;
- begin
- Result := MATValeurSpline(matX, t, FNb);
- end;
- function TCubicSpline.SplineY(const t: Single): Single;
- begin
- Result := MATValeurSpline(matY, t, FNb);
- end;
- function TCubicSpline.SplineZ(const t: Single): Single;
- begin
- Result := MATValeurSpline(matZ, t, FNb);
- end;
- function TCubicSpline.SplineW(const t: Single): Single;
- begin
- Result := MATValeurSpline(matW, t, FNb);
- end;
- procedure TCubicSpline.SplineXY(const t: Single; out X, Y: Single);
- begin
- X := MATValeurSpline(matX, t, FNb);
- Y := MATValeurSpline(matY, t, FNb);
- end;
- procedure TCubicSpline.SplineXYZ(const t: Single; out X, Y, Z: Single);
- begin
- X := MATValeurSpline(matX, t, FNb);
- Y := MATValeurSpline(matY, t, FNb);
- Z := MATValeurSpline(matZ, t, FNb);
- end;
- procedure TCubicSpline.SplineXYZW(const t: Single; out X, Y, Z, W: Single);
- begin
- X := MATValeurSpline(matX, t, FNb);
- Y := MATValeurSpline(matY, t, FNb);
- Z := MATValeurSpline(matZ, t, FNb);
- W := MATValeurSpline(matW, t, FNb);
- end;
- function TCubicSpline.SplineAffineVector(const t: Single): TAffineVector;
- begin
- Result.X := MATValeurSpline(matX, t, FNb);
- Result.Y := MATValeurSpline(matY, t, FNb);
- Result.Z := MATValeurSpline(matZ, t, FNb);
- end;
- procedure TCubicSpline.SplineAffineVector(const t: Single;
- var vector: TAffineVector);
- begin
- vector.X := MATValeurSpline(matX, t, FNb);
- vector.Y := MATValeurSpline(matY, t, FNb);
- vector.Z := MATValeurSpline(matZ, t, FNb);
- end;
- function TCubicSpline.SplineVector(const t: Single): TGLVector;
- begin
- Result.X := MATValeurSpline(matX, t, FNb);
- Result.Y := MATValeurSpline(matY, t, FNb);
- Result.Z := MATValeurSpline(matZ, t, FNb);
- Result.W := MATValeurSpline(matW, t, FNb);
- end;
- procedure TCubicSpline.SplineVector(const t: Single; var vector: TGLVector);
- begin
- vector.X := MATValeurSpline(matX, t, FNb);
- vector.Y := MATValeurSpline(matY, t, FNb);
- vector.Z := MATValeurSpline(matZ, t, FNb);
- vector.W := MATValeurSpline(matW, t, FNb);
- end;
- function TCubicSpline.SplineSlopeX(const t: Single): Single;
- begin
- Result := MATValeurSplineSlope(matX, t, FNb);
- end;
- function TCubicSpline.SplineSlopeY(const t: Single): Single;
- begin
- Result := MATValeurSplineSlope(matY, t, FNb);
- end;
- function TCubicSpline.SplineSlopeZ(const t: Single): Single;
- begin
- Result := MATValeurSplineSlope(matZ, t, FNb);
- end;
- function TCubicSpline.SplineSlopeW(const t: Single): Single;
- begin
- Result := MATValeurSplineSlope(matW, t, FNb);
- end;
- function TCubicSpline.SplineSlopeVector(const t: Single): TAffineVector;
- begin
- Result.X := MATValeurSplineSlope(matX, t, FNb);
- Result.Y := MATValeurSplineSlope(matY, t, FNb);
- Result.Z := MATValeurSplineSlope(matZ, t, FNb);
- end;
- function TCubicSpline.SplineIntersecYZ(X: Single; var Y, Z: Single): Boolean;
- var
- Sup, Inf, Mid: Single;
- SSup, Sinf, Smid: Single;
- begin
- Result := False;
- Sup := FNb;
- Inf := 0.0;
- SSup := SplineX(Sup);
- Sinf := SplineX(Inf);
- if SSup > Sinf then
- begin
- if (SSup < X) or (Sinf > X) then
- Exit;
- while Abs(SSup - Sinf) > 1E-4 do
- begin
- Mid := (Sup + Inf) * 0.5;
- Smid := SplineX(Mid);
- if X < Smid then
- begin
- SSup := Smid;
- Sup := Mid;
- end
- else
- begin
- Sinf := Smid;
- Inf := Mid;
- end;
- end;
- Y := SplineY((Sup + Inf) * 0.5);
- Z := SplineZ((Sup + Inf) * 0.5);
- end
- else
- begin
- if (Sinf < X) or (SSup > X) then
- Exit;
- while Abs(SSup - Sinf) > 1E-4 do
- begin
- Mid := (Sup + Inf) * 0.5;
- Smid := SplineX(Mid);
- if X < Smid then
- begin
- Sinf := Smid;
- Inf := Mid;
- end
- else
- begin
- SSup := Smid;
- Sup := Mid;
- end;
- end;
- Y := SplineY((Sup + Inf) * 0.5);
- Z := SplineZ((Sup + Inf) * 0.5);
- end;
- Result := True;
- end;
- function TCubicSpline.SplineIntersecXZ(Y: Single; var X, Z: Single): Boolean;
- var
- Sup, Inf, Mid: Single;
- SSup, Sinf, Smid: Single;
- begin
- Result := False;
- Sup := FNb;
- Inf := 0.0;
- SSup := SplineY(Sup);
- Sinf := SplineY(Inf);
- if SSup > Sinf then
- begin
- if (SSup < Y) or (Sinf > Y) then
- Exit;
- while Abs(SSup - Sinf) > 1E-4 do
- begin
- Mid := (Sup + Inf) * 0.5;
- Smid := SplineY(Mid);
- if Y < Smid then
- begin
- SSup := Smid;
- Sup := Mid;
- end
- else
- begin
- Sinf := Smid;
- Inf := Mid;
- end;
- end;
- X := SplineX((Sup + Inf) * 0.5);
- Z := SplineZ((Sup + Inf) * 0.5);
- end
- else
- begin
- if (Sinf < Y) or (SSup > Y) then
- Exit;
- while Abs(SSup - Sinf) > 1E-4 do
- begin
- Mid := (Sup + Inf) * 0.5;
- Smid := SplineY(Mid);
- if Y < Smid then
- begin
- Sinf := Smid;
- Inf := Mid;
- end
- else
- begin
- SSup := Smid;
- Sup := Mid;
- end;
- end;
- X := SplineX((Sup + Inf) * 0.5);
- Z := SplineZ((Sup + Inf) * 0.5);
- end;
- Result := True;
- end;
- function TCubicSpline.SplineIntersecXY(Z: Single; var X, Y: Single): Boolean;
- var
- Sup, Inf, Mid: Single;
- SSup, Sinf, Smid: Single;
- begin
- Result := False;
- Sup := FNb;
- Inf := 0.0;
- SSup := SplineZ(Sup);
- Sinf := SplineZ(Inf);
- if SSup > Sinf then
- begin
- if (SSup < Z) or (Sinf > Z) then
- Exit;
- while Abs(SSup - Sinf) > 1E-4 do
- begin
- Mid := (Sup + Inf) * 0.5;
- Smid := SplineZ(Mid);
- if Z < Smid then
- begin
- SSup := Smid;
- Sup := Mid;
- end
- else
- begin
- Sinf := Smid;
- Inf := Mid;
- end;
- end;
- X := SplineX((Sup + Inf) * 0.5);
- Y := SplineY((Sup + Inf) * 0.5);
- end
- else
- begin
- if (Sinf < Z) or (SSup > Z) then
- Exit;
- while Abs(SSup - Sinf) > 1E-4 do
- begin
- Mid := (Sup + Inf) * 0.5;
- Smid := SplineZ(Mid);
- if Z < Smid then
- begin
- Sinf := Smid;
- Inf := Mid;
- end
- else
- begin
- SSup := Smid;
- Sup := Mid;
- end;
- end;
- X := SplineX((Sup + Inf) * 0.5);
- Y := SplineY((Sup + Inf) * 0.5);
- end;
- Result := True;
- end;
- //--------------------------------------------------------------------------
- end.
|