unit Spheres;

interface
uses classes,vectors,dialogs,math;
const SphereRadius=20; //Standardradius einer Kugel
const StaticSpheresInArrayLog=11; //Logarithmus zur Basis 2 der Anzahl der festen Kugeln
      StaticSpheresInArray=1 shl StaticSpheresInArrayLog; //Anzahl der festen Kugeln im Array
      MovingSpheresInArrayLog=11; //Log zur Basis 2 der Anzahl der beweglichen Kugeln
      MovingSpheresInArray=1 shl MovingSpheresInArrayLog; //Anzahl der beweglichen Kugeln im Array
type
  TStaticSphereRec= record  //Kugelrecord
    pos:TVector2Int; //Mittelpunkt
    r:byte;          //Radius
  end;
  PStaticSphereRec=^TStaticSphereRec;//Zeiger auf Kugelstruktur
  TStaticSphere=PStaticSphereRec; //Kürzerer Name für den Zeiger
  //Kugelarray
  TStaticSphereArray=array[0..StaticSpheresInArray-1] of TStaticSphereRec;
  PStaticSphereArray=^TStaticSphereArray;

  TMovingSphereRec= record
    pos:TVector2Int; //Startposition (wird aber auch temporär als aktuelle benutzt)
    r:byte;          //Radius
    dir:TVector2Float; //Bewegungsrichtung

    currentTime:single;  //Zurückgelegte Entfernung
    currentPos:TVector2Int; //aktuelle Position

    kollisionTime:single; //Entfernung bis zum Kollisionspunkt
    kollisionPos:TVector2Int; //Mittelpunkt nach Kollision
  end;
  PMovingSphereRec=^TMovingSphereRec;
  TMovingSphere=PMovingSphereRec;
  //Kugelarray
  TMovingSphereArray=array[0..(MovingSpheresInArray-1)] of TMovingSphereRec;
  PMovingSphereArray=^TMovingSphereArray;


  TSphereList=class
  private
    _countS:integer; //Anzahl der vorhandenen festen Kugeln
    //Es werden je StaticSpheresInArray 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: PStaticSphereArray; //letztes Array
    //Ebenso bei den beweglichen Kugeln
    _countM:integer;
    arraysM:TList;
    lastArrayM: PMovingSphereArray;
  public
    function countMoving:integer; //Anzahl der beweglichen Kugeln
    function createMovingSphere(): TMovingSphere; //neue Kugel erstellen
    function getMovingSphere(i:integer):TMovingSphere;//Kugel holen
    procedure deleteMovingSphere(i:integer);//Kugel löschen

    function countStatic:integer; //Anzahl der festen Kugeln
    function createStaticSphere(): TStaticSphere; //neue Kugel erstellen
    function getStaticSphere(i:integer):TStaticSphere; //Kugel holen
    procedure deleteStaticSphere(i:integer);//Kugel löschen
    procedure convertMovingToStatic(i:integer);//bewegliche Kugeln in eine feste
                                               //umwandeln, indem die Einträge
                                               //übernommen werden, die es in
                                               //beiden gibt

    constructor create;
    destructor destroy;override;

  end;
implementation
//Anzahl der Kugeln
function TSphereList.countMoving:integer;
begin
  result:=_countM;
end;

//Feste Kugel erstellen
function TSphereList.createMovingSphere(): TMovingSphere;
begin
  //Ist das Array voll
  if _countM shr MovingSpheresInArrayLog > arraysM.Count-1 then begin
    //ein neues Array erzeugen
    GetMem(lastArrayM,sizeof(TMovingSphereArray));
    arraysM.Add(lastArrayM);
  end;
  //Die letzte Kugel im Array zurückgeben (der vordere Teil von countS wird
  //abgeschnitten)
  Result:=@lastArrayM^[_countM and high(TMovingSphereArray)];
  inc(_countM);
end;

//Kugel Nr. i zurückgeben
function TSphereList.getMovingSphere(i:integer):TMovingSphere;
begin
  Result:=@PMovingSphereArray(arraysM[i shr MovingSpheresInArrayLog])^[i and high(TMovingSphereArray)];
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 TSphereList.deleteMovingSphere(i:integer);
begin
  dec(_countM);
  //Verhältnismäßig langsam, wenn nicht das letzte Element gelöscht werden soll
  if i<_countM then move(PMovingSphereArray(arraysM[_countM shr MovingSpheresInArrayLog])^[_countM and high(TMovingSphereArray)],
                         PMovingSphereArray(arraysM[i shr MovingSpheresInArrayLog])^[i and high(TMovingSphereArray)],
                         sizeof(TMovingSphereRec));
  //Wurde die letzte Kugel in einem Array gelöscht?
  if _countM and high(TMovingSphereArray) =  high(TMovingSphereArray) then
    lastArrayM:=arraysM[i shr MovingSpheresInArrayLog]; //in das vorherige Array löschen
end;

//Anzahl der Kugeln
function TSphereList.countStatic:integer;
begin
  result:=_countS;
end;

//Feste Kugel erstellen
function TSphereList.createStaticSphere(): TStaticSphere;
begin
  //Ist das Array voll
  if _countS shr staticSpheresInArrayLog > arraysS.Count-1 then begin
    //ein neues Array erzeugen
    GetMem(lastArrayS,sizeof(TStaticSphereArray));
    arraysS.Add(lastArrayS);
  end;
  //Die letzte Kugel im Array zurückgeben (der vordere Teil von countS wird
  //abgeschnitten)
  Result:=@lastArrayS^[_countS and high(TStaticSphereArray)];
  inc(_countS);
end;

function TSphereList.getStaticSphere(i:integer):TStaticSphere;
begin
  Result:=@PStaticSphereArray(arraysS[i shr StaticSpheresInArrayLog])^[i and high(TStaticSphereArray)];
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 TSphereList.deleteStaticSphere(i:integer);
begin
  dec(_countS);
  //Verhältnismäßig langsam, wenn nicht das letzte Element gelöscht werden soll
  if i<>_countS then move(PStaticSphereArray(arraysS[_countS shr StaticSpheresInArrayLog])^[_countS and high(TStaticSphereArray)],
                         PStaticSphereArray(arraysS[i shr StaticSpheresInArrayLog])^[i and high(TStaticSphereArray)],
                         sizeof(TStaticSphereRec));
  //Wurde die letzte Kugel in einem Array gelöscht?
  if _countS and high(TStaticSphereArray) =  high(TStaticSphereArray) then
    lastArrayS:=arraysS[i shr StaticSpheresInArrayLog]; //in das vorherige Array löschen

end;

//Wandelt eine feste in ein bewegliche Kugel um
procedure TSphereList.convertMovingToStatic(i:integer);
var Sphere:TStaticSphere;
begin
  Sphere:=createStaticSphere; //Neue feste Kugeln
  with PMovingSphereArray(arraysM[i shr MovingSpheresInArrayLog])^[i and high(TMovingSphereArray)] do begin
    //pos und r übernehmen
    Sphere.pos:=pos;
    Sphere.r:=r;
  end;
  deleteMovingSphere(i); //bewegliche Kugel löschen
end;



constructor TSphereList.create;
begin
  arraysS:=TList.create;
  _countS:=0;
  arraysM:=TList.create;
  _countM:=0;
end;
destructor TSphereList.destroy;
var i:integer;
begin
  for i:=0 to arraysS.Count-1 do
    FreeMem (arraysS[i],sizeof(TStaticSphereArray));
  arraysS.free;
  for i:=0 to arraysM.Count-1 do
    FreeMem (arraysM[i],sizeof(TMovingSphereArray));
  arraysM.free;
  inherited;
end;
end.
