unit Spheres3D;

interface
uses classes,vectors,dialogs,math;
const SphereRadius=20; //Standardradius einer Kugel
      StaticSpheres3DInArrayLog=11; //Logarithmus zur Basis 2 der Kugelanzahl pro Array
      StaticSpheres3DInArray=1 shl StaticSpheres3DInArrayLog; //Anzahl der Kugeln
type
  T3DStaticSphereRec= record //Kugelstruktur
    pos:TVector3Int; //Position der Kugel
    r:byte; //Radius der Kugel
  end;
  P3DStaticSphereRec=^T3DStaticSphereRec; //Zeiger auf Kugelstruktur
  T3DStaticSphere=P3DStaticSphereRec; //Kürzerer Name für den Zeiger
  //Kugelarray
  T3DStaticSphereArray=array[0..StaticSpheres3DInArray-1] of T3DStaticSphereRec;
  P3DStaticSphereArray=^T3DStaticSphereArray;

  T3DSphereList=class
  private
    _countS:integer; //Anzahl der vorhanden Kugeln
    //Es werden je StaticSpheres3DInArray Kugeln pro Array gespeichert, und
    //die Zeiger auf die Arrays werden in einer Liste gespeichert.
    //Will man auf die Kugel i zugreifen, bestimmen die vorderen Bits der Nummer
    //das Array, und die hinteren die Kugel. Das ist dann so ähnlich wie die
    //Umwandlung von virtuellen Speicheradressen in echte.
    arraysS:TList; //Liste der Arrays
    lastArrayS: P3DStaticSphereArray; //letztes Array
  public
    function countStatic:integer; //Anzahl der Kugeln
    function createStaticSphere(): T3DStaticSphere; //neue Kugel erstellen
    function getStaticSphere(i:integer):T3DStaticSphere; //Kugel holen
    procedure deleteStaticSphere(i:integer); //Kugel löschen

    constructor create;
    destructor destroy;override;
  end;
implementation

//Anzahl der Kugeln
function T3DSphereList.countStatic:integer;
begin
  result:=_countS;
end;
//neue Kugel erstellen
function T3DSphereList.createStaticSphere(): T3DStaticSphere;
begin
  //Ist das Array voll
  if _countS shr StaticSpheres3DInArrayLog > arraysS.Count-1 then begin
    //ein neues Array erzeugen
    GetMem(lastArrayS,sizeof(T3DStaticSphereArray));
    arraysS.Add(lastArrayS);
  end;
  //Die letzte Kugel im Array zurückgeben (der vordere Teil von countS wird
  //abgeschnitten)
  Result:=@lastArrayS^[_countS and high(T3DStaticSphereArray)];
  inc(_countS);
end;

//Kugel Nr. i zurückgeben
function T3DSphereList.getStaticSphere(i:integer):T3DStaticSphere;
begin
  Result:=@P3DStaticSphereArray(
       arraysS[i shr StaticSpheres3DInArrayLog])^ //Array (vorderer Teil der Nr.)
       [i and high(T3DStaticSphereArray)]; //Kugeln im Array (hinterer Teil)
end;

//Kugel Nr. i löschen, indem die letzte Kugel an die Stelle i verschoben wird.
//Der Nachteil an dem Verfahren ist, das nie ein Array wirklich gelöscht wird,
//solange nicht die ganze Klasse freigegeben wird.
procedure T3DSphereList.deleteStaticSphere(i:integer);
begin
  dec(_countS);
  //Verhältnismäßig langsam, wenn nicht das letzte Element gelöscht werden soll
  if i<>_countS then
    move(P3DStaticSphereArray(arraysS[_countS shr StaticSpheres3DInArrayLog])^[_countS and high(T3DStaticSphereArray)],
         P3DStaticSphereArray(arraysS[i shr StaticSpheres3DInArrayLog])^[i and high(T3DStaticSphereArray)],
        sizeof(T3DStaticSphereRec));
  if _countS and high(T3DStaticSphereArray) =  high(T3DStaticSphereArray) then
    lastArrayS:=arraysS[i shr StaticSpheres3DInArrayLog]; //in das vorherige Array löschen
end;

constructor T3DSphereList.create;
begin
  arraysS:=TList.create; //Kugelliste erzeugen
  _countS:=0;
end;
destructor T3DSphereList.destroy;
var i:integer;
begin
  //Alle Arrays löschen
  for i:=0 to arraysS.Count-1 do
    FreeMem (arraysS[i],sizeof(T3DStaticSphereArray));
  //Liste freigeben
  arraysS.free;
  inherited;
end;
end.
