procedure TFMain.TimerMainTimer(Sender: TObject);
const
  ddt = 1;
  Mu = 1; // Коэффициент трения (исп. где бильярдные шары)
var
  i, j: Integer;
  dx, dy: Real;
  VxOld, VyOld: Real;
{
  fi, adir, bdir, av, bv, vn, dv1, dv2: Real;
//}
{
  vx10,vy10,vx20,vy20,m1,m2: Real;
//}
{
  l,x1,x2,y1,y2: Real;
//}
{
  ab, av, bv, alpha1, gamma1, beta1, alpha2, gamma2, beta2,
  pr1x, pr1y, pr1xx, pr1xy, pr1yx, pr1yy,
  pr2x, pr2y, pr2xx, pr2xy, pr2yx, pr2yy,
  Vx1New, Vy1New, Vx2New, Vy2New: Real;
//}
begin
  for i:=1 to Project.ProjObjectsCount do
  begin
    with Project.ProjObjects[i], Project do
    begin
      if x <= Radius then           // Проверка столкновения с левой границей
      begin
        Vx:=-Vx;
        x:=x + Vx;
      end;
      if x >= ProjWidth-Radius then // с правой границей
      begin
        Vx:=-Vx;
        x:=x + Vx;
      end;
      if y <= Radius then          // с верхней границей
      begin
        Vy:=-Vy;
        y:=y + Vy;
      end;
      if y>=ProjHeight-Radius then // с нижней границей
      begin
        Vy:=-Vy;
        y:=y + Vy;
      end;
      for j:=1 to ProjObjectsCount do
        if j<>i then
        begin
          dx:=x + vx - ProjObjects[j].x - ProjObjects[j].vx;
          dy:=y + vy - ProjObjects[j].y - ProjObjects[j].vy;
          if sqrt(dx*dx + dy*dy)<= Radius+ProjObjects[j].Radius then
          begin
(*
            //
            // Упрощенный вариант
            //
            VxOld:=Vx;
            VyOld:=Vy;
            Vx:=((Mass-ProjObjects[j].Mass)*VxOld
              + 2*ProjObjects[j].Mass*ProjObjects[j].Vx)
              /(Mass+ProjObjects[j].Mass);
            Vy:=((Mass-ProjObjects[j].Mass)*VyOld
              + 2*ProjObjects[j].Mass*ProjObjects[j].Vy)
              /(Mass+ProjObjects[j].Mass);
            ProjObjects[j].Vx:=((ProjObjects[j].Mass-Mass)
              *ProjObjects[j].Vx + 2*Mass*VxOld)/(Mass+ProjObjects[j].Mass);
            ProjObjects[j].Vy:=((ProjObjects[j].Mass-Mass)
              *ProjObjects[j].Vy + 2*Mass*VyOld)/(Mass+ProjObjects[j].Mass);
//*)
(*
            //
            // Другой вариант
            //
            VxOld:=Vx;
            VyOld:=Vy;
            Vx:=(2*ProjObjects[j].Mass*ProjObjects[j].Vx -
              (ProjObjects[j].Mass-Mass)*Vx)
              /(ProjObjects[j].Mass+Mass);
            Vy:=(2*ProjObjects[j].Mass*ProjObjects[j].Vy -
              (ProjObjects[j].Mass-Mass)*Vy)
              /(ProjObjects[j].Mass+Mass);
            ProjObjects[j].Vx:=(2*Mass*VxOld -
              (Mass-ProjObjects[j].Mass)*ProjObjects[j].Vx)
              /(ProjObjects[j].Mass+Mass);
            ProjObjects[j].Vy:=(2*Mass*VyOld -
              (Mass-ProjObjects[j].Mass)*ProjObjects[j].Vy)
              /(ProjObjects[j].Mass+Mass);
//*)
(*
            //
            // Из статьи про бильярднае шары (http://billiard-bsv.narod.ru/)
            //
            ab:=Sqrt((ProjObjects[j].x-x)*(ProjObjects[j].x-x)
                + (ProjObjects[j].y-y)*(ProjObjects[j].y-y));
            av:=Sqrt(Vx*Vx + Vy*Vy);
            bv:=Sqrt(ProjObjects[j].Vx*ProjObjects[j].Vx
                + ProjObjects[j].Vy*ProjObjects[j].Vy);
            //Для первого шара
            if ProjObjects[j].y > y then
//              alpha1:=arctan2(ProjObjects[j].x-x,ab)
              alpha1:=arccos((ProjObjects[j].x-x)/ab)
            else
//              alpha1:=-arctan2(ProjObjects[j].x-x,ab);
              alpha1:=-arccos((ProjObjects[j].x-x)/ab);
            if Vy > 0 then
//              gamma1:=arctan2(Vx,av)
              gamma1:=arccos(Vx/av)
            else
//              gamma1:=-arctan2(Vx,av);
              gamma1:=-arccos(Vx/av);
            beta1:=gamma1-alpha1;
            pr1x:=av*Cos(beta1);
            pr1y:=av*Sin(beta1);
            pr1xx:=pr1x*Cos(alpha1);
            pr1xy:=pr1x*Sin(alpha1);
            pr1yx:=pr1y*Cos(alpha1);
            pr1yy:=pr1y*Sin(alpha1);
            //Для второго шара
            if ProjObjects[j].y < y then
//              alpha2:=arctan2(x-ProjObjects[j].x,ab)
              alpha2:=arccos((x-ProjObjects[j].x)/ab)
            else
//              alpha2:=-arctan2(x-ProjObjects[j].x,ab);
              alpha2:=-arccos((x-ProjObjects[j].x)/ab);
            if ProjObjects[j].Vy > 0 then
//              gamma2:=arctan2(ProjObjects[j].Vx,bv)
              gamma2:=arccos(ProjObjects[j].Vx/bv)
            else
//              gamma2:=-arctan2(ProjObjects[j].Vx,bv);
              gamma2:=-arccos(ProjObjects[j].Vx/bv);
            beta2:=gamma2-alpha2;
            pr2x:=bv*Cos(beta2);
            pr2y:=bv*Sin(beta2);
            pr2xx:=pr2x*Cos(alpha2);
            pr2xy:=pr2x*Sin(alpha2);
            pr2yx:=pr2y*Cos(alpha2);
            pr2yy:=pr2y*Sin(alpha2);
            // Вычисление новых составляющих
            Vx1New:=Vx + ((-2*Pr1Xx)+Pr2Xx) * Mu;
            Vy1New:=Vy + ((-2*Pr1Xy)+Pr2Xy) * Mu;
            Vx2New:=ProjObjects[j].Vx + ((-2*Pr2Xx)+Pr1Xx) * Mu;
            Vy2New:=ProjObjects[j].Vy + ((-2*Pr2Xy)+Pr1Xy) * Mu;

            Vx:=Vx1New;
            Vy:=Vy1New;
            ProjObjects[j].Vx:=Vx2New;
            ProjObjects[j].Vy:=Vy2New;
//*)
(*
            //
            // Из флешевого ролика.
            //
            fi:=ArcTan2(ProjObjects[j].y-y, ProjObjects[j].x-x);
            adir:=ArcTan2(Vy,Vx);
            bdir:=ArcTan2(ProjObjects[j].Vy, ProjObjects[j].Vx);
            av:=Sqrt(Vx*Vx + Vy*Vy);
            bv:=Sqrt(ProjObjects[j].Vx*ProjObjects[j].Vx
                + ProjObjects[j].Vy*ProjObjects[j].Vy);
            vn:=av*sin(adir-fi+PI/2) - bv*sin(bdir-fi+PI/2);

            dv1:=-2*ProjObjects[j].Mass / (Mass+ProjObjects[j].Mass)*vn;
            dv2:=2*Mass / (Mass+ProjObjects[j].Mass)*vn;

            Vx:= Vx + dv1*cos(fi);
            Vy:= Vy + dv1*sin(fi);
            ProjObjects[j].Vx:=ProjObjects[j].Vx + dv2*cos(fi);
            ProjObjects[j].Vy:=ProjObjects[j].Vy + dv2*sin(fi);
//*)
(*
            //
            // Из учебника. Частный случай. Центральное столкновение
            //
            VyOld:=Vy;
            Vy:=VyOld*(Mass-ProjObjects[j].Mass)/
              (ProjObjects[j].Mass+Mass) +
              ProjObjects[j].Vy*(2*ProjObjects[j].Mass)/
              (ProjObjects[j].Mass+Mass);
            ProjObjects[j].Vy:=VyOld*(2*Mass)/
              (ProjObjects[j].Mass+Mass) +
              ProjObjects[j].Vy*(ProjObjects[j].Mass-Mass)/
              (ProjObjects[j].Mass+Mass);
//*)
(*
            //
            // Из того же учебника. Частный случай. Центральное столкновение.
            // Пытался подогнать результаты :(
            //
            VyOld:=Vy;
            if(VyOld*ProjObjects[j].Vy >= 0)then
            begin
              Vy:=(2*ProjObjects[j].Mass*ProjObjects[j].Vy -
                (ProjObjects[j].Mass-Mass)*Vy)
                /(ProjObjects[j].Mass+Mass);
              ProjObjects[j].Vy:=(2*Mass*VyOld -
                (Mass-ProjObjects[j].Mass)*ProjObjects[j].Vy)
                /(ProjObjects[j].Mass+Mass);
            end
            else
            if(VyOld*ProjObjects[j].Vy < 0)then
            begin
              Vy:=(2*ProjObjects[j].Mass*ProjObjects[j].Vy -
                Abs((ProjObjects[j].Mass-Mass)*Vy))
                /(ProjObjects[j].Mass+Mass);
              ProjObjects[j].Vy:=(2*Mass*VyOld -
                Abs((Mass-ProjObjects[j].Mass)*ProjObjects[j].Vy))
                /(ProjObjects[j].Mass+Mass);
            end;
//*)
(*
            //
            // Из форума "Всё о Паскале", Подфорум "Написание игр",
            // Тема "Супер Бильярд", Сообщение #2
            // (http://forum.pascal.net.ru/index.php?showtopic=4685)
            //
            vx10 := Vx;             { начальные значения скоростей }
            vy10 := Vy;
            vx20 := ProjObjects[j].Vx;
            vy20 := ProjObjects[j].Vy;
            m1:=Mass;
            m2:=ProjObjects[j].Mass;
            { скорость первого шара после удара }
            Vx := round((2*m2*vx20 + (m1-m2)*vx10)/(m1+m2));
            Vy := round((2*m2*vy20 + (m1-m2)*vy10)/(m1+m2));
            { скорость второго шара после удара }
            ProjObjects[j].Vx := round((2*m1*vx10 + (m2-m1)*vx20)/(m1+m2));
            ProjObjects[j].Vy := round((2*m1*vy10 + (m2-m1)*vy20)/(m1+m2));
//*)
(*
            //
            // Из форума "Всё о Паскале", Подфорум "Написание игр",
            // Тема "Супер Бильярд", Сообщение #14
            // (http://forum.pascal.net.ru/index.php?showtopic=4685)
            //
            x1:=x;
            x2:=ProjObjects[j].x;
            y1:=y;
            y2:=ProjObjects[j].y;
            l:=Arctan((y2-y1)/(x2-x1));
            vx10 := Vx;
            vy10 := Vy;
            vx20 := ProjObjects[j].Vx;
            vy20 := ProjObjects[j].Vy;
            Vx:=(vx20+vy20)*cos(l);
            Vy:=(vx20+vy20)*sin(l);
            ProjObjects[j].Vx:=(vx10+vy10)*cos(l+PI);
            ProjObjects[j].Vy:=(vx10+vy10)*sin(l+PI)
//*)
          end;
        end;
      Vx:=Vx + Fx / Mass;
      Vy:=Vy + Fy / Mass;
      x:=x + Vx * ddt;
      y:=y + Vy * ddt;
    end;
  end;
end;