{ ****************************************************************************** }
{ * geometry 3D Advance library writen by QQ 600585@qq.com                     * }
{ * https://zpascal.net                                                        * }
{ * https://github.com/PassByYou888/zAI                                        * }
{ * https://github.com/PassByYou888/ZServer4D                                  * }
{ * https://github.com/PassByYou888/PascalString                               * }
{ * https://github.com/PassByYou888/zRasterization                             * }
{ * https://github.com/PassByYou888/CoreCipher                                 * }
{ * https://github.com/PassByYou888/zSound                                     * }
{ * https://github.com/PassByYou888/zChinese                                   * }
{ * https://github.com/PassByYou888/zExpression                                * }
{ * https://github.com/PassByYou888/zGameWare                                  * }
{ * https://github.com/PassByYou888/zAnalysis                                  * }
{ * https://github.com/PassByYou888/FFMPEG-Header                              * }
{ * https://github.com/PassByYou888/zTranslate                                 * }
{ * https://github.com/PassByYou888/InfiniteIoT                                * }
{ * https://github.com/PassByYou888/FastMD5                                    * }
{ ****************************************************************************** }

unit Geometry3DUnit;

{$DEFINE FPC_DELPHI_MODE}
{$INCLUDE zDefine.inc}

interface

uses Types, CoreClasses,
  GeometryLib, Geometry2DUnit, PascalStrings, UnicodeMixedLib;

type
  TMat4 = TMatrix;
  TVec4 = TVector;
  TVec3 = TAffineVector;

  TMatrix4 = record
    Buff: TMat4;
  public
    class operator Equal(const Lhs, Rhs: TMatrix4): Boolean;
    class operator NotEqual(const Lhs, Rhs: TMatrix4): Boolean;
    class operator Multiply(const Lhs, Rhs: TMatrix4): TMatrix4;
    class operator Implicit(Value: TGeoFloat): TMatrix4;
    class operator Implicit(Value: TMat4): TMatrix4;

    function Swap: TMatrix4;
    function Lerp(M: TMatrix4; Delta: TGeoFloat): TMatrix4;
    function AffineMatrix: TAffineMatrix;
    function Invert: TMatrix4;
    function Translate(v: TVec3): TMatrix4;
    function Normalize: TMatrix4;
    function Transpose: TMatrix4;
    function AnglePreservingInvert: TMatrix4;
    function Determinant: TGeoFloat;
    function Adjoint: TMatrix4;
    function Pitch(angle: TGeoFloat): TMatrix4;
    function Roll(angle: TGeoFloat): TMatrix4;
    function Turn(angle: TGeoFloat): TMatrix4;
  end;

  TVector4 = record
    Buff: TVec4;
  private
    function GetVec3: TVec3;
    procedure SetVec3(const Value: TVec3);
    function GetVec2: TVec2;
    procedure SetVec2(const Value: TVec2);
    function GetLinkValue(index: Integer): TGeoFloat;
    procedure SetLinkValue(index: Integer; const Value: TGeoFloat);
  public
    property Vec2: TVec2 read GetVec2 write SetVec2;
    property Vec3: TVec3 read GetVec3 write SetVec3;
    property XYZ: TVec3 read GetVec3 write SetVec3;
    property RGB: TVec3 read GetVec3 write SetVec3;
    property Vec4: TVec4 read Buff write Buff;
    property RGBA: TVec4 read Buff write Buff;
    property COLOR: TVec4 read Buff write Buff;
    property LinkValue[index: Integer]: TGeoFloat read GetLinkValue write SetLinkValue; default;

    class operator Equal(const Lhs, Rhs: TVector4): Boolean;
    class operator NotEqual(const Lhs, Rhs: TVector4): Boolean;
    class operator GreaterThan(const Lhs, Rhs: TVector4): Boolean;
    class operator GreaterThanOrEqual(const Lhs, Rhs: TVector4): Boolean;
    class operator LessThan(const Lhs, Rhs: TVector4): Boolean;
    class operator LessThanOrEqual(const Lhs, Rhs: TVector4): Boolean;

    class operator Add(const Lhs, Rhs: TVector4): TVector4;
    class operator Add(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
    class operator Add(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;

    class operator Subtract(const Lhs, Rhs: TVector4): TVector4;
    class operator Subtract(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
    class operator Subtract(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;

    class operator Multiply(const Lhs, Rhs: TVector4): TVector4;
    class operator Multiply(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
    class operator Multiply(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;
    class operator Multiply(const Lhs: TVector4; const Rhs: TMatrix4): TVector4;
    class operator Multiply(const Lhs: TMatrix4; const Rhs: TVector4): TVector4;
    class operator Multiply(const Lhs: TVector4; const Rhs: TMat4): TVector4;
    class operator Multiply(const Lhs: TMat4; const Rhs: TVector4): TVector4;
    class operator Multiply(const Lhs: TVector4; const Rhs: TAffineMatrix): TVector4;
    class operator Multiply(const Lhs: TAffineMatrix; const Rhs: TVector4): TVector4;

    class operator Divide(const Lhs, Rhs: TVector4): TVector4;
    class operator Divide(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
    class operator Divide(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;

    class operator Implicit(Value: TGeoFloat): TVector4;
    class operator Implicit(Value: TVec4): TVector4;
    class operator Implicit(Value: TVec3): TVector4;
    class operator Implicit(Value: TVec2): TVector4;

    class operator Explicit(Value: TVector4): TVec4;
    class operator Explicit(Value: TVector4): TVec3;
    class operator Explicit(Value: TVector4): TVec2;

    procedure SetRGBA(const r, g, b, a: TGeoFloat); overload;
    procedure SetLocation(const fx, fy, fz, fw: TGeoFloat); overload;
    procedure SetLocation(const fx, fy, fz: TGeoFloat); overload;
    function Distance4D(const v2: TVector4): TGeoFloat;
    function Distance3D(const v2: TVector4): TGeoFloat;
    function Distance2D(const v2: TVector4): TGeoFloat;
    function Lerp(const v2: TVector4; const t: TGeoFloat): TVector4;
    function LerpDistance(const v2: TVector4; const d: TGeoFloat): TVector4;
    function Norm: TGeoFloat;
    function length: TGeoFloat;
    function Normalize: TVector4;
    function Cross(const v2: TVector4): TVector4; overload;
    function Cross(const v2: TVec3): TVector4; overload;
    function Cross(const v2: TVec4): TVector4; overload;
  end;

  TVector3 = record
    Buff: TVec3;
  private
    function GetVec2: TVec2;
    procedure SetVec2(const Value: TVec2);
    function GetLinkValue(index: Integer): TGeoFloat;
    procedure SetLinkValue(index: Integer; const Value: TGeoFloat);
  public
    property Vec2: TVec2 read GetVec2 write SetVec2;
    property Vec3: TVec3 read Buff write Buff;
    property XYZ: TVec3 read Buff write Buff;
    property COLOR: TVec3 read Buff write Buff;
    property RGB: TVec3 read Buff write Buff;
    property LinkValue[index: Integer]: TGeoFloat read GetLinkValue write SetLinkValue; default;

    class operator Equal(const Lhs, Rhs: TVector3): Boolean;
    class operator NotEqual(const Lhs, Rhs: TVector3): Boolean;
    class operator GreaterThan(const Lhs, Rhs: TVector3): Boolean;
    class operator GreaterThanOrEqual(const Lhs, Rhs: TVector3): Boolean;
    class operator LessThan(const Lhs, Rhs: TVector3): Boolean;
    class operator LessThanOrEqual(const Lhs, Rhs: TVector3): Boolean;

    class operator Add(const Lhs, Rhs: TVector3): TVector3;
    class operator Add(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
    class operator Add(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;

    class operator Subtract(const Lhs, Rhs: TVector3): TVector3;
    class operator Subtract(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
    class operator Subtract(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;

    class operator Multiply(const Lhs, Rhs: TVector3): TVector3;
    class operator Multiply(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
    class operator Multiply(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;

    class operator Multiply(const Lhs: TVector3; const Rhs: TMatrix4): TVector3;
    class operator Multiply(const Lhs: TMatrix4; const Rhs: TVector3): TVector3;

    class operator Multiply(const Lhs: TVector3; const Rhs: TMat4): TVector3;
    class operator Multiply(const Lhs: TMat4; const Rhs: TVector3): TVector3;

    class operator Multiply(const Lhs: TVector3; const Rhs: TAffineMatrix): TVector3;
    class operator Multiply(const Lhs: TAffineMatrix; const Rhs: TVector3): TVector3;

    class operator Divide(const Lhs, Rhs: TVector3): TVector3;
    class operator Divide(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
    class operator Divide(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;

    class operator Implicit(Value: TGeoFloat): TVector3;
    class operator Implicit(Value: TVec4): TVector3;
    class operator Implicit(Value: TVec3): TVector3;
    class operator Implicit(Value: TVec2): TVector3;

    class operator Explicit(Value: TVector3): TVec4;
    class operator Explicit(Value: TVector3): TVec3;
    class operator Explicit(Value: TVector3): TVec2;

    procedure SetLocation(const fx, fy, fz: TGeoFloat); overload;
    function Distance3D(const v2: TVector3): TGeoFloat;
    function Distance2D(const v2: TVector3): TGeoFloat;
    function Lerp(const v2: TVector3; const t: TGeoFloat): TVector3;
    function LerpDistance(const v2: TVector3; const d: TGeoFloat): TVector3;
    function Norm: TGeoFloat;
    function length: TGeoFloat;
    function Normalize: TVector3;
    function Cross(const v2: TVector3): TVector3;

    function Vec4(fw: TGeoFloat): TVector4; overload;
    function Vec4: TVector4; overload;
  end;

  TAABB = record
    Min, Max: TAffineVector;
  public
    { : Resize the AABB if necessary to include p. }
    procedure Include(const p: TVector3);
    { : Make an AABB that is formed by sweeping a sphere (or AABB) from Start to Dest }
    procedure FromSweep(const Start, dest: TVector3; const radius: TGeoFloat);
    { : Returns the intersection AABB of two AABBs.<p>
      If the AABBs don't intersect, will return a degenerated AABB (plane, line or point). }
    function Intersection(const aabb2: TAABB): TAABB;
    { : Adds delta to min and max of the AABB. }
    procedure Offset(const Delta: TVector3);
    { : Checks if a point "p" is inside an AABB }
    function PointIn(const p: TVector3): Boolean;
  end;

  TVector2 = record
    Buff: TVec2;
  private
    function GetLinkValue(index: Integer): TGeoFloat;
    procedure SetLinkValue(index: Integer; const Value: TGeoFloat);
  public
    property LinkValue[index: Integer]: TGeoFloat read GetLinkValue write SetLinkValue; default;

    class operator Equal(const Lhs, Rhs: TVector2): Boolean;
    class operator NotEqual(const Lhs, Rhs: TVector2): Boolean;
    class operator GreaterThan(const Lhs, Rhs: TVector2): Boolean;
    class operator GreaterThanOrEqual(const Lhs, Rhs: TVector2): Boolean;
    class operator LessThan(const Lhs, Rhs: TVector2): Boolean;
    class operator LessThanOrEqual(const Lhs, Rhs: TVector2): Boolean;

    class operator Add(const Lhs, Rhs: TVector2): TVector2;
    class operator Add(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
    class operator Add(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;

    class operator Subtract(const Lhs, Rhs: TVector2): TVector2;
    class operator Subtract(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
    class operator Subtract(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;

    class operator Multiply(const Lhs, Rhs: TVector2): TVector2;
    class operator Multiply(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
    class operator Multiply(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;

    class operator Divide(const Lhs, Rhs: TVector2): TVector2;
    class operator Divide(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
    class operator Divide(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;

    class operator Implicit(Value: TGeoFloat): TVector2;
    class operator Implicit(Value: TPoint): TVector2;
    class operator Implicit(Value: TPointf): TVector2;
    class operator Implicit(Value: TVec2): TVector2;

    class operator Explicit(Value: TVector2): TPointf;
    class operator Explicit(Value: TVector2): TPoint;
    class operator Explicit(Value: TVector2): TVec2;

    procedure SetLocation(const fx, fy: TGeoFloat); overload;
    function Distance(const v2: TVector2): TGeoFloat;
    function Lerp(const v2: TVector2; const t: TGeoFloat): TVector2;
    function LerpDistance(const v2: TVector2; const d: TGeoFloat): TVector2;
    function Norm: TGeoFloat;
    function length: TGeoFloat;
    function Normalize: TVector2;
  end;

function Vector4(x, y, z, w: TGeoFloat): TVector4; overload;
function Vector4(x, y, z: TGeoFloat): TVector4; overload;
function Vector4(v: TVec3): TVector4; overload;
function Vector4(v: TVec4): TVector4; overload;

function Vector3(x, y, z: TGeoFloat): TVector3; overload;
function Vector3(v: TVec3): TVector3; overload;
function Vector3(v: TVec4): TVector3; overload;

function Vec3(const x, y, z: TGeoFloat): TVec3; overload;
function Vec3(const v: TVec4): TVec3; overload;
function Vec3(const v: TVector3): TVec3; overload;
function Vec3(const v: TVector2): TVec3; overload;
function Vec3(const v: TVector2; z: TGeoFloat): TVec3; overload;

function Vec4(const x, y, z: TGeoFloat): TVec4; overload;
function Vec4(const x, y, z, w: TGeoFloat): TVec4; overload;
function Vec4(const v: TVec3): TVec4; overload;
function Vec4(const v: TVec3; const z: TGeoFloat): TVec4; overload;
function Vec4(const v: TVector3): TVec4; overload;

function Vec2(const v: TVec3): TVector2; overload;
function Vec2(const v: TVec4): TVector2; overload;
function Vec2(const v: TVector3): TVector2; overload;
function Vec2(const v: TVector4): TVector2; overload;

function VecToStr(const v: TVec2): SystemString; overload;
function VecToStr(const v: TVector2): SystemString; overload;
function VecToStr(const v: TArrayVec2): SystemString; overload;
function VecToStr(const v: TVec3): SystemString; overload;
function VecToStr(const v: TVec4): SystemString; overload;
function VecToStr(const v: TVector3): SystemString; overload;
function VecToStr(const v: TVector4): SystemString; overload;
function RectToStr(const v: TRectV2): SystemString; overload;
function RectToStr(const v: TRect): SystemString; overload;

function StrToVec2(const s: SystemString): TVec2;
function StrToVector2(const s: SystemString): TVector2;
function StrToArrayVec2(const s: SystemString): TArrayVec2;
function StrToVec3(const s: SystemString): TVec3;
function StrToVec4(const s: SystemString): TVec4;
function StrToVector3(const s: SystemString): TVector3;
function StrToVector4(const s: SystemString): TVector4;
function StrToRect(const s: SystemString): TRect;
function StrToRectV2(const s: SystemString): TRectV2;

function GetMin(const arry: array of TGeoFloat): TGeoFloat; overload;
function GetMin(const arry: array of Integer): Integer; overload;
function GetMax(const arry: array of TGeoFloat): TGeoFloat; overload;
function GetMax(const arry: array of Integer): Integer; overload;

function FinalAngle4FMX(const a: TGeoFloat): TGeoFloat;
function CalcAngle(const v1, v2: TVec2): TGeoFloat;
function AngleDistance(const sour, dest: TGeoFloat): TGeoFloat;
function SmoothAngle(const sour, dest, Delta: TGeoFloat): TGeoFloat;
function AngleEqual(const a1, a2: TGeoFloat): Boolean;

function Distance(const v1, v2: TVec2): TGeoFloat; overload;
function Distance(const v1, v2: TRectV2): TGeoFloat; overload;

function MovementLerp(const s, d, Lerp: TGeoFloat): TGeoFloat; overload;
function MovementLerp(const s, d: TVec2; Lerp: TGeoFloat): TVec2; overload;
function MovementLerp(const s, d: TRectV2; Lerp: TGeoFloat): TRectV2; overload;

function MovementDistance(const s, d: TVec2; dt: TGeoFloat): TVec2; overload;
function MovementDistance(const s, d: TRectV2; dt: TGeoFloat): TRectV2; overload;
function MovementDistance(const sour, dest: TVector4; Distance: TGeoFloat): TVector4; overload;
function MovementDistance(const sour, dest: TVector3; Distance: TGeoFloat): TVector3; overload;

function MovementDistanceDeltaTime(const s, d: TVec2; Speed_: TGeoFloat): Double; overload;
function MovementDistanceDeltaTime(const s, d: TRectV2; Speed_: TGeoFloat): Double; overload;
function AngleRollDistanceDeltaTime(const s, d: TGeoFloat; ARollSpeed: TGeoFloat): Double; overload;

function BounceVector(const Current: TVector4; DeltaDistance: TGeoFloat; const BeginVector, EndVector: TVector4; var EndFlag: Boolean): TVector4; overload;
function BounceVector(const Current: TVector3; DeltaDistance: TGeoFloat; const BeginVector, EndVector: TVector3; var EndFlag: Boolean): TVector3; overload;
function BounceVector(const Current: TVector2; DeltaDistance: TGeoFloat; const BeginVector, EndVector: TVector2; var EndFlag: Boolean): TVector2; overload;
function BounceFloat(const CurrentVal, DeltaVal, StartVal, OverVal: TGeoFloat; var EndFlag: Boolean): TGeoFloat; overload;

implementation

function Vector4(x, y, z, w: TGeoFloat): TVector4;
begin
  Result.Buff[0] := x;
  Result.Buff[1] := y;
  Result.Buff[2] := z;
  Result.Buff[3] := w;
end;

function Vector4(x, y, z: TGeoFloat): TVector4;
begin
  Result.Buff[0] := x;
  Result.Buff[1] := y;
  Result.Buff[2] := z;
  Result.Buff[3] := 0;
end;

function Vector4(v: TVec3): TVector4;
begin
  Result.Buff[0] := v[0];
  Result.Buff[1] := v[1];
  Result.Buff[2] := v[2];
  Result.Buff[3] := 0;
end;

function Vector4(v: TVec4): TVector4;
begin
  Result.Buff := v;
end;

function Vector3(x, y, z: TGeoFloat): TVector3;
begin
  Result.Buff[0] := x;
  Result.Buff[1] := y;
  Result.Buff[2] := z;
end;

function Vector3(v: TVec3): TVector3;
begin
  Result.Buff := v;
end;

function Vector3(v: TVec4): TVector3;
begin
  Result.Buff[0] := v[0];
  Result.Buff[1] := v[1];
  Result.Buff[2] := v[2];
end;

function Vec3(const x, y, z: TGeoFloat): TVec3;
begin
  Result := AffineVectorMake(x, y, z);
end;

function Vec3(const v: TVec4): TVec3;
begin
  Result[0] := v[0];
  Result[1] := v[1];
  Result[2] := v[2];
end;

function Vec3(const v: TVector3): TVec3;
begin
  Result := v.Buff;
end;

function Vec3(const v: TVector2): TVec3;
begin
  Result[0] := v[0];
  Result[1] := v[1];
  Result[2] := 0;
end;

function Vec3(const v: TVector2; z: TGeoFloat): TVec3;
begin
  Result[0] := v[0];
  Result[1] := v[1];
  Result[2] := z;
end;

function Vec4(const x, y, z: TGeoFloat): TVec4;
begin
  Result := VectorMake(x, y, z, 0);
end;

function Vec4(const x, y, z, w: TGeoFloat): TVec4;
begin
  Result := VectorMake(x, y, z, w);
end;

function Vec4(const v: TVec3): TVec4;
begin
  Result := VectorMake(v);
end;

function Vec4(const v: TVec3; const z: TGeoFloat): TVec4;
begin
  Result := VectorMake(v, z);
end;

function Vec4(const v: TVector3): TVec4;
begin
  Result := VectorMake(v.Buff);
end;

function Vec2(const v: TVec3): TVector2;
begin
  Result := Vec2(v[0], v[1]);
end;

function Vec2(const v: TVec4): TVector2;
begin
  Result := Vec2(v[0], v[1]);
end;

function Vec2(const v: TVector3): TVector2;
begin
  Result[0] := v.Buff[0];
  Result[1] := v.Buff[1];
end;

function Vec2(const v: TVector4): TVector2;
begin
  Result[0] := v.Buff[0];
  Result[1] := v.Buff[1];
end;

function VecToStr(const v: TVec2): SystemString;
begin
  Result := PFormat('%g,%g', [v[0], v[1]]);
end;

function VecToStr(const v: TVector2): SystemString;
begin
  Result := PFormat('%g,%g', [v[0], v[1]]);
end;

function VecToStr(const v: TArrayVec2): SystemString;
var
  i: Integer;
begin
  Result := '';
  for i := low(v) to high(v) do
      Result := Result + if_(i <> low(v), ',', '') + PFormat('%g,%g', [v[i, 0], v[i, 1]]);
end;

function VecToStr(const v: TVec3): SystemString;
begin
  Result := PFormat('%g,%g,%g', [v[0], v[1], v[2]]);
end;

function VecToStr(const v: TVec4): SystemString;
begin
  Result := PFormat('%g,%g,%g,%g', [v[0], v[1], v[2], v[3]]);
end;

function VecToStr(const v: TVector3): SystemString;
begin
  Result := VecToStr(v.Buff);
end;

function VecToStr(const v: TVector4): SystemString;
begin
  Result := VecToStr(v.Buff);
end;

function RectToStr(const v: TRectV2): SystemString;
begin
  Result := PFormat('%g,%g,%g,%g', [v[0][0], v[0][1], v[1][0], v[1][1]]);
end;

function RectToStr(const v: TRect): SystemString;
begin
  Result := PFormat('%d,%d,%d,%d', [v.Left, v.Top, v.Right, v.Bottom]);
end;

function StrToVec2(const s: SystemString): TVec2;
var
  v, v1, v2: U_String;
begin
  v := umlTrimSpace(s);
  v1 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v2 := umlGetFirstStr(v, ',: ');

  Result[0] := umlStrToFloat(v1, 0);
  Result[1] := umlStrToFloat(v2, 0);
end;

function StrToVector2(const s: SystemString): TVector2;
var
  v, v1, v2: U_String;
begin
  v := umlTrimSpace(s);
  v1 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v2 := umlGetFirstStr(v, ',: ');

  Result[0] := umlStrToFloat(v1, 0);
  Result[1] := umlStrToFloat(v2, 0);
end;

function StrToArrayVec2(const s: SystemString): TArrayVec2;
var
  n, v1, v2: U_String;
  L: TVec2List;
begin
  L := TVec2List.Create;
  n := umlTrimSpace(s);
  while n.L > 0 do
    begin
      v1 := umlGetFirstStr(n, ',: ');
      n := umlDeleteFirstStr(n, ',: ');
      v2 := umlGetFirstStr(n, ',: ');
      n := umlDeleteFirstStr(n, ',: ');
      L.Add(umlStrToFloat(v1, 0), umlStrToFloat(v2, 0));
    end;
  Result := L.BuildArray();
  DisposeObject(L);
end;

function StrToVec3(const s: SystemString): TVec3;
var
  v, v1, v2, v3: U_String;
begin
  v := umlTrimSpace(s);
  v1 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v2 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v3 := umlGetFirstStr(v, ',: ');

  Result[0] := umlStrToFloat(v1, 0);
  Result[1] := umlStrToFloat(v2, 0);
  Result[2] := umlStrToFloat(v3, 0);
end;

function StrToVec4(const s: SystemString): TVec4;
var
  v, v1, v2, v3, v4: U_String;
begin
  v := umlTrimSpace(s);
  v1 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v2 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v3 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v4 := umlGetFirstStr(v, ',: ');

  Result[0] := umlStrToFloat(v1, 0);
  Result[1] := umlStrToFloat(v2, 0);
  Result[2] := umlStrToFloat(v3, 0);
  Result[3] := umlStrToFloat(v4, 0);
end;

function StrToVector3(const s: SystemString): TVector3;
var
  v, v1, v2, v3: U_String;
begin
  v := umlTrimSpace(s);
  v1 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v2 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v3 := umlGetFirstStr(v, ',: ');

  Result.Buff[0] := umlStrToFloat(v1, 0);
  Result.Buff[1] := umlStrToFloat(v2, 0);
  Result.Buff[2] := umlStrToFloat(v3, 0);
end;

function StrToVector4(const s: SystemString): TVector4;
var
  v, v1, v2, v3, v4: U_String;
begin
  v := umlTrimSpace(s);
  v1 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v2 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v3 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v4 := umlGetFirstStr(v, ',: ');

  Result.Buff[0] := umlStrToFloat(v1, 0);
  Result.Buff[1] := umlStrToFloat(v2, 0);
  Result.Buff[2] := umlStrToFloat(v3, 0);
  Result.Buff[3] := umlStrToFloat(v4, 0);
end;

function StrToRect(const s: SystemString): TRect;
begin
  Result := Rect2Rect(StrToRectV2(s));
end;

function StrToRectV2(const s: SystemString): TRectV2;
var
  v, v1, v2, v3, v4: U_String;
begin
  v := umlTrimSpace(s);
  v1 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v2 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v3 := umlGetFirstStr(v, ',: ');
  v := umlDeleteFirstStr(v, ',: ');
  v4 := umlGetFirstStr(v, ',: ');

  Result[0][0] := umlStrToFloat(v1, 0);
  Result[0][1] := umlStrToFloat(v2, 0);
  Result[1][0] := umlStrToFloat(v3, 0);
  Result[1][1] := umlStrToFloat(v4, 0);
end;

function GetMin(const arry: array of TGeoFloat): TGeoFloat;
var
  i: Integer;
begin
  Result := arry[low(arry)];
  for i := low(arry) + 1 to high(arry) do
    if Result > arry[i] then
        Result := arry[i];
end;

function GetMin(const arry: array of Integer): Integer;
var
  i: Integer;
begin
  Result := arry[low(arry)];
  for i := low(arry) + 1 to high(arry) do
    if Result > arry[i] then
        Result := arry[i];
end;

function GetMax(const arry: array of TGeoFloat): TGeoFloat;
var
  i: Integer;
begin
  Result := arry[low(arry)];
  for i := low(arry) + 1 to high(arry) do
    if Result < arry[i] then
        Result := arry[i];
end;

function GetMax(const arry: array of Integer): Integer;
var
  i: Integer;
begin
  Result := arry[low(arry)];
  for i := low(arry) + 1 to high(arry) do
    if Result < arry[i] then
        Result := arry[i];
end;

function FinalAngle4FMX(const a: TGeoFloat): TGeoFloat;
begin
  Result := NormalizeDegAngle((-a - 90) + 180);
end;

function CalcAngle(const v1, v2: TVec2): TGeoFloat;
begin
  if IsEqual(v1, v2) then
      Result := 0
  else
      Result := RadToDeg(ArcTan2(v1[0] - v2[0], v1[1] - v2[1]));
end;

function AngleDistance(const sour, dest: TGeoFloat): TGeoFloat;
begin
  Result := Abs(sour - dest);
  if Result > 180.0 then
      Result := 360.0 - Result;
end;

function SmoothAngle(const sour, dest, Delta: TGeoFloat): TGeoFloat;
var
  a1, a2: TGeoFloat;
begin
  if sour <> dest then
    begin
      if sour >= 0 then
        begin
          a1 := sour + Delta;
          a2 := sour - Delta;
        end
      else
        begin
          a1 := sour + -Delta;
          a2 := sour + Delta;
        end;

      if AngleDistance(dest, a1) >= AngleDistance(dest, a2) then
        begin
          if AngleDistance(dest, a2) > Delta then
              Result := a2
          else
              Result := dest;
        end
      else if AngleDistance(dest, a1) > Delta then
          Result := a1
      else
          Result := dest;
    end
  else
      Result := dest;
end;

function AngleEqual(const a1, a2: TGeoFloat): Boolean;
begin
  Result := AngleDistance(a1, a2) < 0.01;
end;

function Distance(const v1, v2: TVec2): TGeoFloat;
begin
  Result := PointDistance(v1, v2);
end;

function Distance(const v1, v2: TRectV2): TGeoFloat;
var
  d1, d2: TGeoFloat;
begin
  d1 := PointDistance(v1[0], v2[0]);
  d2 := PointDistance(v1[1], v2[1]);
  if d1 >= d2 then
      Result := d1
  else
      Result := d2;
end;

function MovementLerp(const s, d, Lerp: TGeoFloat): TGeoFloat;
begin
  if Lerp < 1.0 then
      Result := s + Lerp * (d - s)
  else
      Result := d;
end;

function MovementLerp(const s, d: TVec2; Lerp: TGeoFloat): TVec2;
begin
  if Lerp < 1.0 then
    begin
      Result[0] := s[0] + Lerp * (d[0] - s[0]);
      Result[1] := s[1] + Lerp * (d[1] - s[1]);
    end
  else
      Result := d;
end;

function MovementLerp(const s, d: TRectV2; Lerp: TGeoFloat): TRectV2;
begin
  if Lerp < 1.0 then
    begin
      Result[0] := MovementLerp(s[0], d[0], Lerp);
      Result[1] := MovementLerp(s[1], d[1], Lerp);
    end
  else
      Result := d;
end;

function MovementDistance(const s, d: TVec2; dt: TGeoFloat): TVec2;
var
  k: Double;
begin
  k := dt / Sqrt((d[0] - s[0]) * (d[0] - s[0]) + (d[1] - s[1]) * (d[1] - s[1]));
  Result[0] := s[0] + k * (d[0] - s[0]);
  Result[1] := s[1] + k * (d[1] - s[1]);
end;

function MovementDistance(const s, d: TRectV2; dt: TGeoFloat): TRectV2;
begin
  if Distance(s[0], d[0]) > dt then
      Result[0] := MovementDistance(s[0], d[0], dt)
  else
      Result[0] := d[0];

  if Distance(s[1], d[1]) > dt then
      Result[1] := MovementDistance(s[1], d[1], dt)
  else
      Result[1] := d[1];
end;

function MovementDistance(const sour, dest: TVector4; Distance: TGeoFloat): TVector4;
var
  k: TGeoFloat;
begin
  // calc distance
  k := Distance / Sqrt((dest[0] - sour[0]) * (dest[0] - sour[0]) + (dest[1] - sour[1]) * (dest[1] - sour[1]) + (dest[2] - sour[2]) * (dest[2] - sour[2]) + (dest[3] - sour[3]) *
    (dest[3] - sour[3]));
  // done
  Result[0] := sour[0] + k * (dest[0] - sour[0]);
  Result[1] := sour[1] + k * (dest[1] - sour[1]);
  Result[2] := sour[2] + k * (dest[2] - sour[2]);
  Result[3] := sour[3] + k * (dest[3] - sour[3]);
end;

function MovementDistance(const sour, dest: TVector3; Distance: TGeoFloat): TVector3;
var
  k: TGeoFloat;
begin
  // calc distance
  k := Distance / Sqrt((dest[0] - sour[0]) * (dest[0] - sour[0]) + (dest[1] - sour[1]) * (dest[1] - sour[1]) + (dest[2] - sour[2]) * (dest[2] - sour[2]));
  // done
  Result[0] := sour[0] + k * (dest[0] - sour[0]);
  Result[1] := sour[1] + k * (dest[1] - sour[1]);
  Result[2] := sour[2] + k * (dest[2] - sour[2]);
end;

function MovementDistanceDeltaTime(const s, d: TVec2; Speed_: TGeoFloat): Double;
begin
  Result := Distance(s, d) / Speed_;
end;

function MovementDistanceDeltaTime(const s, d: TRectV2; Speed_: TGeoFloat): Double;
var
  d1, d2: Double;
begin
  d1 := MovementDistanceDeltaTime(s[0], d[0], Speed_);
  d2 := MovementDistanceDeltaTime(s[1], d[1], Speed_);
  if d1 > d2 then
      Result := d1
  else
      Result := d2;
end;

function AngleRollDistanceDeltaTime(const s, d: TGeoFloat; ARollSpeed: TGeoFloat): Double;
begin
  Result := AngleDistance(s, d) / ARollSpeed;
end;

function BounceVector(const Current: TVector4; DeltaDistance: TGeoFloat; const BeginVector, EndVector: TVector4; var EndFlag: Boolean): TVector4;
  function ToVector: TVector4;
  begin
    if EndFlag then
        Result := EndVector
    else
        Result := BeginVector;
  end;

var
  k: TGeoFloat;
begin
  k := Current.Distance4D(ToVector);
  if k >= DeltaDistance then
      Result := MovementDistance(Current, ToVector, DeltaDistance)
  else
    begin
      Result := ToVector;
      EndFlag := not EndFlag;
      Result := MovementDistance(Result, ToVector, DeltaDistance - k);
    end;
end;

function BounceVector(const Current: TVector3; DeltaDistance: TGeoFloat; const BeginVector, EndVector: TVector3; var EndFlag: Boolean): TVector3;
  function ToVector: TVector3;
  begin
    if EndFlag then
        Result := EndVector
    else
        Result := BeginVector;
  end;

var
  k: TGeoFloat;
begin
  k := Current.Distance3D(ToVector);
  if k >= DeltaDistance then
      Result := MovementDistance(Current, ToVector, DeltaDistance)
  else
    begin
      Result := ToVector;
      EndFlag := not EndFlag;
      Result := MovementDistance(Result, ToVector, DeltaDistance - k);
    end;
end;

function BounceVector(const Current: TVector2; DeltaDistance: TGeoFloat; const BeginVector, EndVector: TVector2; var EndFlag: Boolean): TVector2;
  function ToVector: TVector2;
  begin
    if EndFlag then
        Result := EndVector
    else
        Result := BeginVector;
  end;

var
  k: TGeoFloat;
begin
  k := Vec2Distance(Current.Buff, ToVector.Buff);
  if k >= DeltaDistance then
      Result := Vec2LerpTo(Current.Buff, ToVector.Buff, DeltaDistance)
  else
    begin
      Result := ToVector;
      EndFlag := not EndFlag;
      Result := Vec2LerpTo(Result.Buff, ToVector.Buff, DeltaDistance - k);
    end;
end;

function BounceFloat(const CurrentVal, DeltaVal, StartVal, OverVal: TGeoFloat; var EndFlag: Boolean): TGeoFloat;
  function IfOut(Cur, Delta, dest: TGeoFloat): Boolean;
  begin
    if Cur > dest then
        Result := Cur - Delta < dest
    else
        Result := Cur + Delta > dest;
  end;

  function GetOutValue(Cur, Delta, dest: TGeoFloat): TGeoFloat;
  begin
    if IfOut(Cur, Delta, dest) then
      begin
        if Cur > dest then
            Result := dest - (Cur - Delta)
        else
            Result := Cur + Delta - dest;
      end
    else
        Result := 0;
  end;

  function GetDeltaValue(Cur, Delta, dest: TGeoFloat): TGeoFloat;
  begin
    if Cur > dest then
        Result := Cur - Delta
    else
        Result := Cur + Delta;
  end;

begin
  if (DeltaVal > 0) and (StartVal <> OverVal) then
    begin
      if EndFlag then
        begin
          if IfOut(CurrentVal, DeltaVal, OverVal) then
            begin
              EndFlag := False;
              Result := umlProcessCycleValue(OverVal, GetOutValue(CurrentVal, DeltaVal, OverVal), StartVal, OverVal, EndFlag);
            end
          else
              Result := GetDeltaValue(CurrentVal, DeltaVal, OverVal);
        end
      else
        begin
          if IfOut(CurrentVal, DeltaVal, StartVal) then
            begin
              EndFlag := True;
              Result := umlProcessCycleValue(StartVal, GetOutValue(CurrentVal, DeltaVal, StartVal), StartVal, OverVal, EndFlag);
            end
          else
              Result := GetDeltaValue(CurrentVal, DeltaVal, StartVal);
        end
    end
  else
      Result := CurrentVal;
end;

class operator TMatrix4.Equal(const Lhs, Rhs: TMatrix4): Boolean;
begin
  Result := VectorEquals(Lhs.Buff[0], Rhs.Buff[0]) and VectorEquals(Lhs.Buff[1], Rhs.Buff[1]) and VectorEquals(Lhs.Buff[2], Rhs.Buff[2]) and VectorEquals(Lhs.Buff[3], Rhs.Buff[3]);
end;

class operator TMatrix4.NotEqual(const Lhs, Rhs: TMatrix4): Boolean;
begin
  Result := not(Lhs = Rhs);
end;

class operator TMatrix4.Multiply(const Lhs, Rhs: TMatrix4): TMatrix4;
begin
  Result.Buff := GeometryLib.MatrixMultiply(Lhs.Buff, Rhs.Buff);
end;

class operator TMatrix4.Implicit(Value: TGeoFloat): TMatrix4;
var
  i, j: Integer;
begin
  for i := 0 to 3 do
    for j := 0 to 3 do
        Result.Buff[i, j] := Value;
end;

class operator TMatrix4.Implicit(Value: TMat4): TMatrix4;
begin
  Result.Buff := Value;
end;

function TMatrix4.Swap: TMatrix4;
var
  i, j: Integer;
begin
  for i := 0 to 3 do
    for j := 0 to 3 do
        Result.Buff[j, i] := Buff[i, j];
end;

function TMatrix4.Lerp(M: TMatrix4; Delta: TGeoFloat): TMatrix4;
var
  i, j: Integer;
begin
  for j := 0 to 3 do
    for i := 0 to 3 do
        Result.Buff[i][j] := Buff[i][j] + (M.Buff[i][j] - Buff[i][j]) * Delta;
end;

function TMatrix4.AffineMatrix: TAffineMatrix;
begin
  Result[0, 0] := Buff[0, 0];
  Result[0, 1] := Buff[0, 1];
  Result[0, 2] := Buff[0, 2];
  Result[1, 0] := Buff[1, 0];
  Result[1, 1] := Buff[1, 1];
  Result[1, 2] := Buff[1, 2];
  Result[2, 0] := Buff[2, 0];
  Result[2, 1] := Buff[2, 1];
  Result[2, 2] := Buff[2, 2];
end;

function TMatrix4.Invert: TMatrix4;
var
  det: TGeoFloat;
begin
  Result.Buff := Buff;
  det := GeometryLib.MatrixDeterminant(Result.Buff);
  if Abs(det) < Epsilon then
      Result.Buff := GeometryLib.IdentityHmgMatrix
  else
    begin
      GeometryLib.AdjointMatrix(Result.Buff);
      GeometryLib.ScaleMatrix(Result.Buff, 1 / det);
    end;
end;

function TMatrix4.Translate(v: TVec3): TMatrix4;
begin
  Result.Buff := Buff;
  GeometryLib.TranslateMatrix(Result.Buff, v);
end;

function TMatrix4.Normalize: TMatrix4;
begin
  Result.Buff := Buff;
  GeometryLib.NormalizeMatrix(Result.Buff);
end;

function TMatrix4.Transpose: TMatrix4;
begin
  Result.Buff := Buff;
  GeometryLib.TransposeMatrix(Result.Buff);
end;

function TMatrix4.AnglePreservingInvert: TMatrix4;
begin
  Result.Buff := GeometryLib.AnglePreservingMatrixInvert(Buff);
end;

function TMatrix4.Determinant: TGeoFloat;
begin
  Result := GeometryLib.MatrixDeterminant(Buff);
end;

function TMatrix4.Adjoint: TMatrix4;
begin
  Result.Buff := Buff;
  GeometryLib.AdjointMatrix(Result.Buff);
end;

function TMatrix4.Pitch(angle: TGeoFloat): TMatrix4;
begin
  Result.Buff := GeometryLib.Pitch(Buff, angle);
end;

function TMatrix4.Roll(angle: TGeoFloat): TMatrix4;
begin
  Result.Buff := GeometryLib.Roll(Buff, angle);
end;

function TMatrix4.Turn(angle: TGeoFloat): TMatrix4;
begin
  Result.Buff := GeometryLib.Turn(Buff, angle);
end;

function TVector4.GetVec3: TVec3;
begin
  Result := AffineVectorMake(Buff);
end;

procedure TVector4.SetVec3(const Value: TVec3);
begin
  Buff := VectorMake(Value);
end;

function TVector4.GetVec2: TVec2;
begin
  Result[0] := Buff[0];
  Result[1] := Buff[1];
end;

procedure TVector4.SetVec2(const Value: TVec2);
begin
  Buff[0] := Value[0];
  Buff[1] := Value[1];
end;

function TVector4.GetLinkValue(index: Integer): TGeoFloat;
begin
  Result := Buff[index];
end;

procedure TVector4.SetLinkValue(index: Integer; const Value: TGeoFloat);
begin
  Buff[index] := Value;
end;

class operator TVector4.Equal(const Lhs, Rhs: TVector4): Boolean;
begin
  Result := (Lhs.Buff[0] = Rhs.Buff[0]) and (Lhs.Buff[1] = Rhs.Buff[1]) and (Lhs.Buff[2] = Rhs.Buff[2]) and (Lhs.Buff[3] = Rhs.Buff[3]);
end;

class operator TVector4.NotEqual(const Lhs, Rhs: TVector4): Boolean;
begin
  Result := (Lhs.Buff[0] <> Rhs.Buff[0]) or (Lhs.Buff[1] <> Rhs.Buff[1]) or (Lhs.Buff[2] <> Rhs.Buff[2]) or (Lhs.Buff[3] <> Rhs.Buff[3]);
end;

class operator TVector4.GreaterThan(const Lhs, Rhs: TVector4): Boolean;
begin
  Result := (Lhs.Buff[0] > Rhs.Buff[0]) and (Lhs.Buff[1] > Rhs.Buff[1]) and (Lhs.Buff[2] > Rhs.Buff[2]) and (Lhs.Buff[3] > Rhs.Buff[3]);
end;

class operator TVector4.GreaterThanOrEqual(const Lhs, Rhs: TVector4): Boolean;
begin
  Result := (Lhs.Buff[0] >= Rhs.Buff[0]) and (Lhs.Buff[1] >= Rhs.Buff[1]) and (Lhs.Buff[2] >= Rhs.Buff[2]) and (Lhs.Buff[3] >= Rhs.Buff[3]);
end;

class operator TVector4.LessThan(const Lhs, Rhs: TVector4): Boolean;
begin
  Result := (Lhs.Buff[0] < Rhs.Buff[0]) and (Lhs.Buff[1] < Rhs.Buff[1]) and (Lhs.Buff[2] < Rhs.Buff[2]) and (Lhs.Buff[3] < Rhs.Buff[3]);
end;

class operator TVector4.LessThanOrEqual(const Lhs, Rhs: TVector4): Boolean;
begin
  Result := (Lhs.Buff[0] <= Rhs.Buff[0]) and (Lhs.Buff[1] <= Rhs.Buff[1]) and (Lhs.Buff[2] <= Rhs.Buff[2]) and (Lhs.Buff[3] <= Rhs.Buff[3]);
end;

class operator TVector4.Add(const Lhs, Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] + Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] + Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] + Rhs.Buff[2];
  Result.Buff[3] := Lhs.Buff[3] + Rhs.Buff[3];
end;

class operator TVector4.Add(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] + Rhs;
  Result.Buff[1] := Lhs.Buff[1] + Rhs;
  Result.Buff[2] := Lhs.Buff[2] + Rhs;
  Result.Buff[3] := Lhs.Buff[3] + Rhs;
end;

class operator TVector4.Add(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs + Rhs.Buff[0];
  Result.Buff[1] := Lhs + Rhs.Buff[1];
  Result.Buff[2] := Lhs + Rhs.Buff[2];
  Result.Buff[3] := Lhs + Rhs.Buff[3];
end;

class operator TVector4.Subtract(const Lhs, Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] - Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] - Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] - Rhs.Buff[2];
  Result.Buff[3] := Lhs.Buff[3] - Rhs.Buff[3];
end;

class operator TVector4.Subtract(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] - Rhs;
  Result.Buff[1] := Lhs.Buff[1] - Rhs;
  Result.Buff[2] := Lhs.Buff[2] - Rhs;
  Result.Buff[3] := Lhs.Buff[3] - Rhs;
end;

class operator TVector4.Subtract(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs - Rhs.Buff[0];
  Result.Buff[1] := Lhs - Rhs.Buff[1];
  Result.Buff[2] := Lhs - Rhs.Buff[2];
  Result.Buff[3] := Lhs - Rhs.Buff[3];
end;

class operator TVector4.Multiply(const Lhs, Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] * Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] * Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] * Rhs.Buff[2];
  Result.Buff[3] := Lhs.Buff[2] * Rhs.Buff[3];
end;

class operator TVector4.Multiply(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] * Rhs;
  Result.Buff[1] := Lhs.Buff[1] * Rhs;
  Result.Buff[2] := Lhs.Buff[2] * Rhs;
  Result.Buff[3] := Lhs.Buff[3] * Rhs;
end;

class operator TVector4.Multiply(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs * Rhs.Buff[0];
  Result.Buff[1] := Lhs * Rhs.Buff[1];
  Result.Buff[2] := Lhs * Rhs.Buff[2];
  Result.Buff[3] := Lhs * Rhs.Buff[3];
end;

class operator TVector4.Multiply(const Lhs: TVector4; const Rhs: TMatrix4): TVector4;
begin
  Result.Buff := GeometryLib.VectorTransform(Lhs.Buff, Rhs.Buff);
end;

class operator TVector4.Multiply(const Lhs: TMatrix4; const Rhs: TVector4): TVector4;
begin
  Result.Buff := VectorTransform(Rhs.Buff, Lhs.Buff);
end;

class operator TVector4.Multiply(const Lhs: TVector4; const Rhs: TMat4): TVector4;
begin
  Result.Buff := GeometryLib.VectorTransform(Lhs.Buff, Rhs);
end;

class operator TVector4.Multiply(const Lhs: TMat4; const Rhs: TVector4): TVector4;
begin
  Result.Buff := GeometryLib.VectorTransform(Rhs.Buff, Lhs);
end;

class operator TVector4.Multiply(const Lhs: TVector4; const Rhs: TAffineMatrix): TVector4;
begin
  Result.Buff := GeometryLib.VectorTransform(Lhs.Buff, Rhs);
end;

class operator TVector4.Multiply(const Lhs: TAffineMatrix; const Rhs: TVector4): TVector4;
begin
  Result.Buff := GeometryLib.VectorTransform(Rhs.Buff, Lhs);
end;

class operator TVector4.Divide(const Lhs, Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] / Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] / Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] / Rhs.Buff[2];
  Result.Buff[3] := Lhs.Buff[3] / Rhs.Buff[3];
end;

class operator TVector4.Divide(const Lhs: TVector4; const Rhs: TGeoFloat): TVector4;
begin
  Result.Buff[0] := Lhs.Buff[0] / Rhs;
  Result.Buff[1] := Lhs.Buff[1] / Rhs;
  Result.Buff[2] := Lhs.Buff[2] / Rhs;
  Result.Buff[3] := Lhs.Buff[3] / Rhs;
end;

class operator TVector4.Divide(const Lhs: TGeoFloat; const Rhs: TVector4): TVector4;
begin
  Result.Buff[0] := Lhs / Rhs.Buff[0];
  Result.Buff[1] := Lhs / Rhs.Buff[1];
  Result.Buff[2] := Lhs / Rhs.Buff[2];
  Result.Buff[3] := Lhs / Rhs.Buff[3];
end;

class operator TVector4.Implicit(Value: TGeoFloat): TVector4;
begin
  Result.Buff[0] := Value;
  Result.Buff[1] := Value;
  Result.Buff[2] := Value;
  Result.Buff[3] := Value;
end;

class operator TVector4.Implicit(Value: TVec4): TVector4;
begin
  Result.Buff := Value;
end;

class operator TVector4.Implicit(Value: TVec3): TVector4;
begin
  Result.Buff := VectorMake(Value);
end;

class operator TVector4.Implicit(Value: TVec2): TVector4;
begin
  Result.Buff := VectorMake(Value[0], Value[1], 0, 0);
end;

class operator TVector4.Explicit(Value: TVector4): TVec4;
begin
  Result := Value.Buff;
end;

class operator TVector4.Explicit(Value: TVector4): TVec3;
begin
  Result := AffineVectorMake(Value.Buff);
end;

class operator TVector4.Explicit(Value: TVector4): TVec2;
begin
  Result[0] := Value.Buff[0];
  Result[1] := Value.Buff[1];
end;

procedure TVector4.SetRGBA(const r, g, b, a: TGeoFloat);
begin
  Buff[0] := r;
  Buff[1] := g;
  Buff[2] := b;
  Buff[3] := a;
end;

procedure TVector4.SetLocation(const fx, fy, fz, fw: TGeoFloat);
begin
  Buff[0] := fx;
  Buff[1] := fy;
  Buff[2] := fz;
  Buff[3] := fw;
end;

procedure TVector4.SetLocation(const fx, fy, fz: TGeoFloat);
begin
  Buff[0] := fx;
  Buff[1] := fy;
  Buff[2] := fz;
end;

function TVector4.Distance4D(const v2: TVector4): TGeoFloat;
begin
  Result := Sqrt(Sqr(v2.Buff[0] - Buff[0]) + Sqr(v2.Buff[1] - Buff[1]) + Sqr(v2.Buff[2] - Buff[2]) + Sqr(v2.Buff[3] - Buff[3]));
end;

function TVector4.Distance3D(const v2: TVector4): TGeoFloat;
begin
  Result := Sqrt(Sqr(v2.Buff[0] - Buff[0]) + Sqr(v2.Buff[1] - Buff[1]) + Sqr(v2.Buff[2] - Buff[2]));
end;

function TVector4.Distance2D(const v2: TVector4): TGeoFloat;
begin
  Result := Sqrt(Sqr(v2.Buff[0] - Buff[0]) + Sqr(v2.Buff[1] - Buff[1]));
end;

function TVector4.Lerp(const v2: TVector4; const t: TGeoFloat): TVector4;
begin
  Result.Buff[0] := Buff[0] + (v2.Buff[0] - Buff[0]) * t;
  Result.Buff[1] := Buff[1] + (v2.Buff[1] - Buff[1]) * t;
  Result.Buff[2] := Buff[2] + (v2.Buff[2] - Buff[2]) * t;
  Result.Buff[3] := Buff[3] + (v2.Buff[3] - Buff[3]) * t;
end;

function TVector4.LerpDistance(const v2: TVector4; const d: TGeoFloat): TVector4;
var
  k: Double;
begin
  k := d / Sqrt((v2.Buff[0] - Buff[0]) * (v2.Buff[0] - Buff[0]) + (v2.Buff[1] - Buff[1]) * (v2.Buff[1] - Buff[1]) + (v2.Buff[2] - Buff[2]) * (v2.Buff[2] - Buff[2]) +
    (v2.Buff[3] - Buff[3]) * (v2.Buff[3] - Buff[3]));
  Result.Buff[0] := Buff[0] + k * (v2.Buff[0] - Buff[0]);
  Result.Buff[1] := Buff[1] + k * (v2.Buff[1] - Buff[1]);
  Result.Buff[2] := Buff[2] + k * (v2.Buff[2] - Buff[2]);
  Result.Buff[3] := Buff[3] + k * (v2.Buff[3] - Buff[3]);
end;

function TVector4.Norm: TGeoFloat;
begin
  Result := Buff[0] * Buff[0] + Buff[1] * Buff[1] + Buff[2] * Buff[2] + Buff[3] * Buff[3];
end;

function TVector4.length: TGeoFloat;
begin
  Result := Sqrt(Norm);
end;

function TVector4.Normalize: TVector4;
var
  InvLen: TGeoFloat;
  vn: TGeoFloat;
begin
  vn := Norm;
  if vn = 0 then
      Result := Self
  else
    begin
      InvLen := RSqrt(vn);
      Result.Buff[0] := Buff[0] * InvLen;
      Result.Buff[1] := Buff[1] * InvLen;
      Result.Buff[2] := Buff[2] * InvLen;
      Result.Buff[3] := Buff[3] * InvLen;
    end;
end;

function TVector4.Cross(const v2: TVector4): TVector4;
begin
  Result.Buff[0] := Buff[1] * v2.Buff[2] - Buff[2] * v2.Buff[1];
  Result.Buff[1] := Buff[2] * v2.Buff[0] - Buff[0] * v2.Buff[2];
  Result.Buff[2] := Buff[0] * v2.Buff[1] - Buff[1] * v2.Buff[0];
  Result.Buff[3] := 0;
end;

function TVector4.Cross(const v2: TVec3): TVector4;
begin
  Result.Buff[0] := Buff[1] * v2[2] - Buff[2] * v2[1];
  Result.Buff[1] := Buff[2] * v2[0] - Buff[0] * v2[2];
  Result.Buff[2] := Buff[0] * v2[1] - Buff[1] * v2[0];
  Result.Buff[3] := 0;
end;

function TVector4.Cross(const v2: TVec4): TVector4;
begin
  Result.Buff[0] := Buff[1] * v2[2] - Buff[2] * v2[1];
  Result.Buff[1] := Buff[2] * v2[0] - Buff[0] * v2[2];
  Result.Buff[2] := Buff[0] * v2[1] - Buff[1] * v2[0];
  Result.Buff[3] := 0;
end;

function TVector3.GetVec2: TVec2;
begin
  Result[0] := Buff[0];
  Result[1] := Buff[1];
end;

procedure TVector3.SetVec2(const Value: TVec2);
begin
  Buff[0] := Value[0];
  Buff[1] := Value[1];
end;

function TVector3.GetLinkValue(index: Integer): TGeoFloat;
begin
  Result := Buff[index];
end;

procedure TVector3.SetLinkValue(index: Integer; const Value: TGeoFloat);
begin
  Buff[index] := Value;
end;

class operator TVector3.Equal(const Lhs, Rhs: TVector3): Boolean;
begin
  Result := (Lhs.Buff[0] = Rhs.Buff[0]) and (Lhs.Buff[1] = Rhs.Buff[1]) and (Lhs.Buff[2] = Rhs.Buff[2]);
end;

class operator TVector3.NotEqual(const Lhs, Rhs: TVector3): Boolean;
begin
  Result := (Lhs.Buff[0] <> Rhs.Buff[0]) or (Lhs.Buff[1] <> Rhs.Buff[1]) or (Lhs.Buff[2] <> Rhs.Buff[2]);
end;

class operator TVector3.GreaterThan(const Lhs, Rhs: TVector3): Boolean;
begin
  Result := (Lhs.Buff[0] > Rhs.Buff[0]) and (Lhs.Buff[1] > Rhs.Buff[1]) and (Lhs.Buff[2] > Rhs.Buff[2]);
end;

class operator TVector3.GreaterThanOrEqual(const Lhs, Rhs: TVector3): Boolean;
begin
  Result := (Lhs.Buff[0] >= Rhs.Buff[0]) and (Lhs.Buff[1] >= Rhs.Buff[1]) and (Lhs.Buff[2] >= Rhs.Buff[2]);
end;

class operator TVector3.LessThan(const Lhs, Rhs: TVector3): Boolean;
begin
  Result := (Lhs.Buff[0] < Rhs.Buff[0]) and (Lhs.Buff[1] < Rhs.Buff[1]) and (Lhs.Buff[2] < Rhs.Buff[2]);
end;

class operator TVector3.LessThanOrEqual(const Lhs, Rhs: TVector3): Boolean;
begin
  Result := (Lhs.Buff[0] <= Rhs.Buff[0]) and (Lhs.Buff[1] <= Rhs.Buff[1]) and (Lhs.Buff[2] <= Rhs.Buff[2]);
end;

class operator TVector3.Add(const Lhs, Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] + Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] + Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] + Rhs.Buff[2];
end;

class operator TVector3.Add(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] + Rhs;
  Result.Buff[1] := Lhs.Buff[1] + Rhs;
  Result.Buff[2] := Lhs.Buff[2] + Rhs;
end;

class operator TVector3.Add(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs + Rhs.Buff[0];
  Result.Buff[1] := Lhs + Rhs.Buff[1];
  Result.Buff[2] := Lhs + Rhs.Buff[2];
end;

class operator TVector3.Subtract(const Lhs, Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] - Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] - Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] - Rhs.Buff[2];
end;

class operator TVector3.Subtract(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] - Rhs;
  Result.Buff[1] := Lhs.Buff[1] - Rhs;
  Result.Buff[2] := Lhs.Buff[2] - Rhs;
end;

class operator TVector3.Subtract(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs - Rhs.Buff[0];
  Result.Buff[1] := Lhs - Rhs.Buff[1];
  Result.Buff[2] := Lhs - Rhs.Buff[2];
end;

class operator TVector3.Multiply(const Lhs, Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] * Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] * Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] * Rhs.Buff[2];
end;

class operator TVector3.Multiply(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] * Rhs;
  Result.Buff[1] := Lhs.Buff[1] * Rhs;
  Result.Buff[2] := Lhs.Buff[2] * Rhs;
end;

class operator TVector3.Multiply(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs * Rhs.Buff[0];
  Result.Buff[1] := Lhs * Rhs.Buff[1];
  Result.Buff[2] := Lhs * Rhs.Buff[2];
end;

class operator TVector3.Multiply(const Lhs: TVector3; const Rhs: TMatrix4): TVector3;
begin
  Result.Buff := GeometryLib.VectorTransform(Lhs.Buff, Rhs.Buff);
end;

class operator TVector3.Multiply(const Lhs: TMatrix4; const Rhs: TVector3): TVector3;
begin
  Result.Buff := GeometryLib.VectorTransform(Rhs.Buff, Lhs.Buff);
end;

class operator TVector3.Multiply(const Lhs: TVector3; const Rhs: TMat4): TVector3;
begin
  Result.Buff := GeometryLib.VectorTransform(Lhs.Buff, Rhs);
end;

class operator TVector3.Multiply(const Lhs: TMat4; const Rhs: TVector3): TVector3;
begin
  Result.Buff := GeometryLib.VectorTransform(Rhs.Buff, Lhs);
end;

class operator TVector3.Multiply(const Lhs: TVector3; const Rhs: TAffineMatrix): TVector3;
begin
  Result.Buff := GeometryLib.VectorTransform(Lhs.Buff, Rhs);
end;

class operator TVector3.Multiply(const Lhs: TAffineMatrix; const Rhs: TVector3): TVector3;
begin
  Result.Buff := GeometryLib.VectorTransform(Rhs.Buff, Lhs);
end;

class operator TVector3.Divide(const Lhs, Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] / Rhs.Buff[0];
  Result.Buff[1] := Lhs.Buff[1] / Rhs.Buff[1];
  Result.Buff[2] := Lhs.Buff[2] / Rhs.Buff[2];
end;

class operator TVector3.Divide(const Lhs: TVector3; const Rhs: TGeoFloat): TVector3;
begin
  Result.Buff[0] := Lhs.Buff[0] / Rhs;
  Result.Buff[1] := Lhs.Buff[1] / Rhs;
  Result.Buff[2] := Lhs.Buff[2] / Rhs;
end;

class operator TVector3.Divide(const Lhs: TGeoFloat; const Rhs: TVector3): TVector3;
begin
  Result.Buff[0] := Lhs / Rhs.Buff[0];
  Result.Buff[1] := Lhs / Rhs.Buff[1];
  Result.Buff[2] := Lhs / Rhs.Buff[2];
end;

class operator TVector3.Implicit(Value: TGeoFloat): TVector3;
begin
  Result.Buff[0] := Value;
  Result.Buff[1] := Value;
  Result.Buff[2] := Value;
end;

class operator TVector3.Implicit(Value: TVec4): TVector3;
begin
  Result.Buff := AffineVectorMake(Value);
end;

class operator TVector3.Implicit(Value: TVec3): TVector3;
begin
  Result.Buff := Value;
end;

class operator TVector3.Implicit(Value: TVec2): TVector3;
begin
  Result.Buff := AffineVectorMake(Value[0], Value[1], 0);
end;

class operator TVector3.Explicit(Value: TVector3): TVec4;
begin
  Result := VectorMake(Value.Buff);
end;

class operator TVector3.Explicit(Value: TVector3): TVec3;
begin
  Result := Value.Buff;
end;

class operator TVector3.Explicit(Value: TVector3): TVec2;
begin
  Result[0] := Value.Buff[0];
  Result[1] := Value.Buff[1];
end;

procedure TVector3.SetLocation(const fx, fy, fz: TGeoFloat);
begin
  Buff[0] := fx;
  Buff[1] := fy;
  Buff[2] := fz;
end;

function TVector3.Distance3D(const v2: TVector3): TGeoFloat;
begin
  Result := Sqrt(Sqr(v2.Buff[0] - Buff[0]) + Sqr(v2.Buff[1] - Buff[1]) + Sqr(v2.Buff[2] - Buff[2]));
end;

function TVector3.Distance2D(const v2: TVector3): TGeoFloat;
begin
  Result := Sqrt(Sqr(v2.Buff[0] - Buff[0]) + Sqr(v2.Buff[1] - Buff[1]));
end;

function TVector3.Lerp(const v2: TVector3; const t: TGeoFloat): TVector3;
begin
  Result.Buff[0] := Buff[0] + (v2.Buff[0] - Buff[0]) * t;
  Result.Buff[1] := Buff[1] + (v2.Buff[1] - Buff[1]) * t;
  Result.Buff[2] := Buff[2] + (v2.Buff[2] - Buff[2]) * t;
end;

function TVector3.LerpDistance(const v2: TVector3; const d: TGeoFloat): TVector3;
var
  k: Double;
begin
  k := d / Sqrt((v2.Buff[0] - Buff[0]) * (v2.Buff[0] - Buff[0]) + (v2.Buff[1] - Buff[1]) * (v2.Buff[1] - Buff[1]) + (v2.Buff[2] - Buff[2]) * (v2.Buff[2] - Buff[2]));
  Result.Buff[0] := Buff[0] + k * (v2.Buff[0] - Buff[0]);
  Result.Buff[1] := Buff[1] + k * (v2.Buff[1] - Buff[1]);
  Result.Buff[2] := Buff[2] + k * (v2.Buff[2] - Buff[2]);
end;

function TVector3.Norm: TGeoFloat;
begin
  Result := Buff[0] * Buff[0] + Buff[1] * Buff[1] + Buff[2] * Buff[2];
end;

function TVector3.length: TGeoFloat;
begin
  Result := Sqrt(Norm);
end;

function TVector3.Normalize: TVector3;
var
  InvLen: TGeoFloat;
  vn: TGeoFloat;
begin
  vn := Norm;
  if vn = 0 then
      Result := Self
  else
    begin
      InvLen := RSqrt(vn);
      Result.Buff[0] := Buff[0] * InvLen;
      Result.Buff[1] := Buff[1] * InvLen;
      Result.Buff[2] := Buff[2] * InvLen;
    end;
end;

function TVector3.Cross(const v2: TVector3): TVector3;
begin
  Result.Buff[0] := Buff[1] * v2.Buff[2] - Buff[2] * v2.Buff[1];
  Result.Buff[1] := Buff[2] * v2.Buff[0] - Buff[0] * v2.Buff[2];
  Result.Buff[2] := Buff[0] * v2.Buff[1] - Buff[1] * v2.Buff[0];
end;

function TVector3.Vec4(fw: TGeoFloat): TVector4;
begin
  Result.SetLocation(Buff[0], Buff[1], Buff[2], fw);
end;

function TVector3.Vec4: TVector4;
begin
  Result.SetLocation(Buff[0], Buff[1], Buff[2], 0);
end;

{ TAABB }

procedure TAABB.Include(const p: TVector3);
begin
  if p.Buff[0] < Min[0] then
      Min[0] := p.Buff[0];
  if p.Buff[0] > Max[0] then
      Max[0] := p.Buff[0];

  if p.Buff[1] < Min[1] then
      Min[1] := p.Buff[1];
  if p.Buff[1] > Max[1] then
      Max[1] := p.Buff[1];

  if p.Buff[2] < Min[2] then
      Min[2] := p.Buff[2];
  if p.Buff[2] > Max[2] then
      Max[2] := p.Buff[2];
end;

procedure TAABB.FromSweep(const Start, dest: TVector3; const radius: TGeoFloat);
begin
  if Start.Buff[0] < dest.Buff[0] then
    begin
      Min[0] := Start.Buff[0] - radius;
      Max[0] := dest.Buff[0] + radius;
    end
  else
    begin
      Min[0] := dest.Buff[0] - radius;
      Max[0] := Start.Buff[0] + radius;
    end;

  if Start.Buff[1] < dest.Buff[1] then
    begin
      Min[1] := Start.Buff[1] - radius;
      Max[1] := dest.Buff[1] + radius;
    end
  else
    begin
      Min[1] := dest.Buff[1] - radius;
      Max[1] := Start.Buff[1] + radius;
    end;

  if Start.Buff[2] < dest.Buff[2] then
    begin
      Min[2] := Start.Buff[2] - radius;
      Max[2] := dest.Buff[2] + radius;
    end
  else
    begin
      Min[2] := dest.Buff[2] - radius;
      Max[2] := Start.Buff[2] + radius;
    end;
end;

function TAABB.Intersection(const aabb2: TAABB): TAABB;
var
  i: Integer;
begin
  for i := 0 to 2 do
    begin
      Result.Min[i] := MaxFloat(Min[i], aabb2.Min[i]);
      Result.Max[i] := MinFloat(Max[i], aabb2.Max[i]);
    end;
end;

procedure TAABB.Offset(const Delta: TVector3);
begin
  AddVector(Min, Delta.Buff);
  AddVector(Max, Delta.Buff);
end;

function TAABB.PointIn(const p: TVector3): Boolean;
begin
  Result := (p.Buff[0] <= Max[0]) and (p.Buff[0] >= Min[0])
    and (p.Buff[1] <= Max[1]) and (p.Buff[1] >= Min[1])
    and (p.Buff[2] <= Max[2]) and (p.Buff[2] >= Min[2]);
end;

function TVector2.GetLinkValue(index: Integer): TGeoFloat;
begin
  Result := Buff[index];
end;

procedure TVector2.SetLinkValue(index: Integer; const Value: TGeoFloat);
begin
  Buff[index] := Value;
end;

class operator TVector2.Equal(const Lhs, Rhs: TVector2): Boolean;
begin
  Result := IsEqual(Lhs.Buff, Rhs.Buff);
end;

class operator TVector2.NotEqual(const Lhs, Rhs: TVector2): Boolean;
begin
  Result := NotEqual(Lhs.Buff, Rhs.Buff);
end;

class operator TVector2.GreaterThan(const Lhs, Rhs: TVector2): Boolean;
begin
  Result := (Lhs.Buff[0] > Rhs.Buff[0]) and (Lhs.Buff[1] > Rhs.Buff[1]);
end;

class operator TVector2.GreaterThanOrEqual(const Lhs, Rhs: TVector2): Boolean;
begin
  Result := (Lhs.Buff[0] >= Rhs.Buff[0]) and (Lhs.Buff[1] >= Rhs.Buff[1]);
end;

class operator TVector2.LessThan(const Lhs, Rhs: TVector2): Boolean;
begin
  Result := (Lhs.Buff[0] < Rhs.Buff[0]) and (Lhs.Buff[1] < Rhs.Buff[1]);
end;

class operator TVector2.LessThanOrEqual(const Lhs, Rhs: TVector2): Boolean;
begin
  Result := (Lhs.Buff[0] <= Rhs.Buff[0]) and (Lhs.Buff[1] <= Rhs.Buff[1]);
end;

class operator TVector2.Add(const Lhs, Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Add(Lhs.Buff, Rhs.Buff);
end;

class operator TVector2.Add(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
begin
  Result.Buff := Vec2Add(Lhs.Buff, Rhs);
end;

class operator TVector2.Add(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Add(Lhs, Rhs.Buff);
end;

class operator TVector2.Subtract(const Lhs, Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Sub(Lhs.Buff, Rhs.Buff);
end;

class operator TVector2.Subtract(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
begin
  Result.Buff := Vec2Sub(Lhs.Buff, Rhs);
end;

class operator TVector2.Subtract(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Sub(Lhs, Rhs.Buff);
end;

class operator TVector2.Multiply(const Lhs, Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Mul(Lhs.Buff, Rhs.Buff);
end;

class operator TVector2.Multiply(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
begin
  Result.Buff := Vec2Mul(Lhs.Buff, Rhs);
end;

class operator TVector2.Multiply(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Mul(Lhs, Rhs.Buff);
end;

class operator TVector2.Divide(const Lhs, Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Div(Lhs.Buff, Rhs.Buff);
end;

class operator TVector2.Divide(const Lhs: TVector2; const Rhs: TGeoFloat): TVector2;
begin
  Result.Buff := Vec2Div(Lhs.Buff, Rhs);
end;

class operator TVector2.Divide(const Lhs: TGeoFloat; const Rhs: TVector2): TVector2;
begin
  Result.Buff := Vec2Div(Lhs, Rhs.Buff);
end;

class operator TVector2.Implicit(Value: TGeoFloat): TVector2;
begin
  Result.Buff := Vec2(Value);
end;

class operator TVector2.Implicit(Value: TPoint): TVector2;
begin
  Result.Buff := Vec2(Value);
end;

class operator TVector2.Implicit(Value: TPointf): TVector2;
begin
  Result.Buff := Vec2(Value);
end;

class operator TVector2.Implicit(Value: TVec2): TVector2;
begin
  Result.Buff := Value;
end;

class operator TVector2.Explicit(Value: TVector2): TPointf;
begin
  Result := MakePointf(Value.Buff);
end;

class operator TVector2.Explicit(Value: TVector2): TPoint;
begin
  Result := MakePoint(Value.Buff);
end;

class operator TVector2.Explicit(Value: TVector2): TVec2;
begin
  Result := Value.Buff;
end;

procedure TVector2.SetLocation(const fx, fy: TGeoFloat);
begin
  Buff := Vec2(fx, fy);
end;

function TVector2.Distance(const v2: TVector2): TGeoFloat;
begin
  Result := Vec2Distance(Buff, v2.Buff);
end;

function TVector2.Lerp(const v2: TVector2; const t: TGeoFloat): TVector2;
begin
  Result.Buff := Vec2Lerp(Buff, v2.Buff, t);
end;

function TVector2.LerpDistance(const v2: TVector2; const d: TGeoFloat): TVector2;
begin
  Result.Buff := Vec2LerpTo(Buff, v2.Buff, d);
end;

function TVector2.Norm: TGeoFloat;
begin
  Result := Vec2Norm(Buff);
end;

function TVector2.length: TGeoFloat;
begin
  Result := Vec2Length(Buff);
end;

function TVector2.Normalize: TVector2;
begin
  Result.Buff := Vec2Normalize(Buff);
end;

end.
