Unit ArrangeDialog;

{$mode objfpc}{$H+}

Interface

Uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  ExtCtrls, ComCtrls, Buttons, MaskEdit, StdCtrls, Spin, Arrow, Math, CADEngine,
  tpg, Dispatcher;

Type

  { TArrangeForm }
  TPhyzPoint=Class
    M,X,Y,ax,ay:Extended;
    Name:String;
  End;

  TArrangeForm = Class(TForm)
    ArrangeSubjectRadioGroup: TRadioGroup;
    ApplyToCheckGroup: TCheckGroup;
    ArrangeStylePageControl: TPageControl;
    ArrayTabSheet: TTabSheet;
    AutoDHCheckBox: TCheckBox;
    CancelBitBtn: TBitBtn;
    AngVelocityTrackBar: TTrackBar;
    AutoDWCheckBox: TCheckBox;
    ConsiderBothSpeedButton1: TSpeedButton;
    ConsiderInboundSpeedButton1: TSpeedButton;
    ConsiderOutboundSpeedButton: TSpeedButton;
    ConsiderBothSpeedButton: TSpeedButton;
    ConsiderOutboundSpeedButton1: TSpeedButton;
    dHDivFloatSpinEdit: TFloatSpinEdit;
    dRDivFloatSpinEdit: TFloatSpinEdit;
    dFiDivFloatSpinEdit: TFloatSpinEdit;
    dRFloatSpinEdit: TFloatSpinEdit;
    Fi1FloatSpinEdit: TFloatSpinEdit;
    Fi2FloatSpinEdit: TFloatSpinEdit;
    dWDivFloatSpinEdit: TFloatSpinEdit;
    FractalCheckBox: TCheckBox;
    FI0SpinEdit: TFloatSpinEdit;
    dRSpinEdit: TFloatSpinEdit;
    DWFloatSpinEdit: TFloatSpinEdit;
    dHFloatSpinEdit: TFloatSpinEdit;
    CFractalCheckBox: TCheckBox;
    GroupBox1: TGroupBox;
    CHopCountSpinEdit: TSpinEdit;
    Label10: TLabel;
    Label11: TLabel;
    Label12: TLabel;
    Label13: TLabel;
    Label14: TLabel;
    Label15: TLabel;
    Label16: TLabel;
    Label17: TLabel;
    Label18: TLabel;
    Label19: TLabel;
    Label20: TLabel;
    Label21: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    PeekOriginSpeedButton1: TSpeedButton;
    ConcentricRadioButton: TRadioButton;
    QulonRadioButton: TRadioButton;
    GravityRadioButton: TRadioButton;
    RebuildParticleListSpeedButton: TSpeedButton;
    HopCountSpinEdit: TSpinEdit;
    DirectionUpSpeedButton: TSpeedButton;
    DirectionRightSpeedButton: TSpeedButton;
    DirectionLeftSpeedButton: TSpeedButton;
    DirectionDownSpeedButton: TSpeedButton;
    ConsiderInboundSpeedButton: TSpeedButton;
    SpiralRadioButton: TRadioButton;
    RadiusSpinEdit: TFloatSpinEdit;
    dFISpinEdit: TFloatSpinEdit;
    Label5: TLabel;
    OrigX0SpinEdit: TFloatSpinEdit;
    OrigY0SpinEdit: TFloatSpinEdit;
    PeekPointLabel1: TLabel;
    TimeTrackBar: TTrackBar;
    ElasticityTrackBar: TTrackBar;
    UsePlacementOriginCheckBox: TCheckBox;
    PeekPointLabel: TLabel;
    OrigXSpinEdit: TFloatSpinEdit;
    OrigYSpinEdit: TFloatSpinEdit;
    PlaceAtGridCheckBox: TCheckBox;
    ProcessPlaceBitBtn: TBitBtn;
    DxSpinEdit: TFloatSpinEdit;
    DySpinEdit: TFloatSpinEdit;
    MassRadioGroup: TRadioGroup;
    RowCountSpinEdit: TSpinEdit;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    Label4: TLabel;
    Panel1: TPanel;
    PlanetTabSheet: TTabSheet;
    ColCountSpinEdit: TSpinEdit;
    AutoArrangeSpeedButton: TSpeedButton;
    PeekOriginSpeedButton: TSpeedButton;
    PhysicsTabSheet: TTabSheet;
    LinkTreeTabSheet: TTabSheet;
    LinkCircleTabSheet: TTabSheet;
    Procedure CancelBitBtnClick(Sender: TObject);
    procedure ElasticityTrackBarChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    Procedure FormShow(Sender: TObject);
    procedure MassRadioGroupSelectionChanged(Sender: TObject);
    Procedure PeekOriginSpeedButton1Click(Sender: TObject);
    Procedure PeekOriginSpeedButtonClick(Sender: TObject);
    procedure ProcessPlaceBitBtnClick(Sender: TObject);
    procedure RebuildParticleListSpeedButtonClick(Sender: TObject);
    procedure TimeTrackBarChange(Sender: TObject);
  Private
    { private declarations }
  Public
    { public declarations }
  End;

Var
  ArrangeForm: TArrangeForm;
  AList:TStringList; // gravity-acceleration list
  ListBuiltDone:Boolean; //flag for 1-time building of mass-acceleration list
  GravitySetDone:Boolean; //flag for fill gravity from
  ElasticitySetDone:Boolean; //flag for fill elasticity from
  PreProcessed:Boolean; //flag for everything but t^2 computed


Implementation
Uses unit1;
{ TArrangeForm }

Procedure UpdateScreenShell(Drawer:TDrawer);
Var x1,x2,x3,x4,y1,y2,y3,y4,lx,ly,rx,ry:Extended;
Begin
  Drawer.StoW(Form1.DrawingPanel.Left+Form1.Splitter2.Left,Drawer.Wy+Form1.DrawingPanel.Top,x1,y1);
  Drawer.StoW(Form1.DrawingPanel.Left+Form1.Splitter2.Left,0+Form1.DrawingPanel.Top,x2,y2);
  Drawer.StoW(Drawer.Wx+Form1.DrawingPanel.Left,Drawer.Wy+Form1.DrawingPanel.Top,x3,y3);
  Drawer.StoW(Drawer.Wx+Form1.DrawingPanel.Left,0+Form1.DrawingPanel.Top,x4,y4);
  With Scene Do Begin
    ScreenShell.X1:=Math.maxExtended;
    ScreenShell.X2:=-Math.maxExtended+Math.minExtended;
    ScreenShell.Y1:=Math.MaxExtended;
    ScreenShell.Y2:=-Math.maxExtended+Math.minExtended;
    If x1<ScreenShell.X1 Then ScreenShell.X1:=x1;
    If x2<ScreenShell.X1 Then ScreenShell.X1:=x2;
    If x3<ScreenShell.X1 Then ScreenShell.X1:=x3;
    If x4<ScreenShell.X1 Then ScreenShell.X1:=x4;
    If x1>ScreenShell.X2 Then ScreenShell.X2:=x1;
    If x2>ScreenShell.X2 Then ScreenShell.X2:=x2;
    If x3>ScreenShell.X2 Then ScreenShell.X2:=x3;
    If x4>ScreenShell.X2 Then ScreenShell.X2:=x4;
    If y1<ScreenShell.Y1 Then ScreenShell.Y1:=y1;
    If y2<ScreenShell.Y1 Then ScreenShell.Y1:=y2;
    If y3<ScreenShell.Y1 Then ScreenShell.Y1:=y3;
    If y4<ScreenShell.Y1 Then ScreenShell.Y1:=y4;
    If y1>ScreenShell.Y2 Then ScreenShell.Y2:=y1;
    If y2>ScreenShell.Y2 Then ScreenShell.Y2:=y2;
    If y3>ScreenShell.Y2 Then ScreenShell.Y2:=y3;
    If y4>ScreenShell.Y2 Then ScreenShell.Y2:=y4;
  end;
End;

procedure TArrangeForm.ProcessPlaceBitBtnClick(Sender: TObject);
Var W,H,dw,ddh,dh,dx,dy,X,Y,GX,GY,MainAxis,SecAxis,MainAxisC,SecAxisC:Extended;
    R,dR,Fi,dFi,X0,Y0,T,Elasticity,AngVelocity,XCenter,YCenter:Extended;
    I,J,K,M,N,Hop,RoundCount,InboundCount,OutBoundCount,RubberSign,MainAxisSign,SecAxisSign:Longint;
    Obj:TVObject;
    Callout:TVCallout;
    Port:TVPort;
    Connection:TVConnection;
    NewShell,SmallShell:TShell;
    ObjName,Obj1Name:String;
    List,LayerList,CurrentLayer,NextLayer,TestLayer:TStringList;
    PlacedEarlier:Boolean;
    O1,O2:TPhyzPoint;
    Obj1,Obj2:TVObject;

Begin
  If (Not (Sender Is TBitBtn)) And Not(AutoArrangeSpeedButton.Down) Then Exit;
  List:=TStringList.Create;
  Case ArrangeSubjectRadioGroup.ItemIndex Of
  0: Begin //selection
       For I:=1 To Form1.SelectionListEditor.RowCount-1 Do
         If (Form1.SelectionListEditor.Cells[0,I]='object') And
            (ApplyToCheckGroup.Checked[0]) Then
               List.Add(Form1.SelectionListEditor.Cells[1,I]);
         If (Form1.SelectionListEditor.Cells[0,I]='callout') And
            (ApplyToCheckGroup.Checked[1]) Then
               List.Add(Form1.SelectionListEditor.Cells[1,I]);
     End;
  1: Begin //visible
       UpdateScreenShell(Scene.Drawer);
       Scene.UpdateVisibility;
       If Scene.VisibleList.Count>0 Then
         For I:=0 To Scene.VisibleList.Count-1 Do Begin
           If (Scene.VisibleList.Objects[I] Is TVObject) And
              (ApplyToCheckGroup.Checked[0]) Then
               List.Add(Scene.VisibleList[I]);
           If (Scene.VisibleList.Objects[I] Is TVCallout) And
              (ApplyToCheckGroup.Checked[1]) Then
               List.Add(Scene.VisibleList[I]);
         End;
     End;
  2: Begin //all
       If ApplyToCheckGroup.Checked[0] Then
         If Scene.VObjectList.Count>0 Then
           For I:=0 To Scene.VObjectList.Count-1 Do
             List.Add(Scene.VObjectList[I]);
       If ApplyToCheckGroup.Checked[1] Then
         If Scene.VCalloutList.Count>0 Then
           For I:=0 To Scene.VCalloutList.Count-1 Do
             List.Add(Scene.VCalloutList[I]);
     End;
  End;
  If List.Count=0 Then Exit;
  UpdateScreenShell(Scene.Drawer);
 If ArrangeStylePageControl.ActivePage=ArrayTabSheet Then Begin //rect array arragement
   N:=ColCountSpinEdit.Value;  M:=RowCountSpinEdit.Value;
   If ((M=0) And (N=0)) Or ((M=1) And (N=1)) Then Begin
     M:=Round(Sqrt(List.Count));
     N:=(List.Count Div M);
     If (List.Count Mod M)>0 Then N:=N+1;
   End;
   NewShell:=Scene.ScreenShell;
   SmallShell.X1:=(NewShell.X1+NewShell.X2)/2-(NewShell.X2-NewShell.X1)*0.8/2;
   SmallShell.X2:=(NewShell.X1+NewShell.X2)/2+(NewShell.X2-NewShell.X1)*0.8/2;
   SmallShell.Y1:=(NewShell.Y1+NewShell.Y2)/2-(NewShell.Y2-NewShell.Y1)*0.8/2;
   SmallShell.Y2:=(NewShell.Y1+NewShell.Y2)/2+(NewShell.Y2-NewShell.Y1)*0.8/2;
   W:=SmallShell.X2-SmallShell.X1;
   H:=SmallShell.Y2-SmallShell.Y1;
   If (DxSpinEdit.Value=0) And (DySpinEdit.Value=0) Then Begin
     If N>1 Then dx:=W/(N-1) Else dx:=0;
     If M>1 Then dy:=H/(M-1) Else dy:=0;
   End Else Begin
     dx:=DxSpinEdit.Value;
     dy:=DySpinEdit.Value;
   End;
   If UsePlacementOriginCheckBox.Checked Then Y:=OrigYSpinEdit.Value Else Y:=SmallShell.Y1;
   K:=0;
   For I:=1 To M Do Begin
     If UsePlacementOriginCheckBox.Checked Then X:=OrigXSpinEdit.Value Else X:=SmallShell.X1;
     For J:=1 To N Do Begin
       If K=List.Count Then Break;
       ObjName:=List[K];
       If PlaceAtGridCheckBox.Checked Then Begin
         Gx:=Form1.GridGapEdit.Value*Round(X/Form1.GridGapEdit.Value);
         Gy:=Form1.GridGapEdit.Value*Round(Y/Form1.GridGapEdit.Value);
       End Else Begin
         GX:=X;GY:=Y;
       End;
       If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
         Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
         Scene.MoveObjectTo(Obj,GX,GY,'');
         Scene.UpdateSelHandles(List[K]);
       end Else
         If Scene.VCalloutList.IndexOf(ObjName)>=0 Then Begin
           Callout:=Scene.VCalloutList.Objects[Scene.VCalloutList.IndexOf(ObjName)] As TVCallout;
           Scene.MoveCalloutTo(Callout,GX,GY,'');
           Scene.UpdateSelHandles(List[K]);
         end;
       Inc(K);
       X:=X+dx;
     End;
     Y:=Y+dy;
   End;
  End; //array arrange
  If ArrangeStylePageControl.ActivePage=PlanetTabSheet Then Begin // planet arragement
    Fi:=Fi0SpinEdit.Value;
    R:=RadiusSpinEdit.Value;
    dFi:=dFISpinEdit.Value;
    dR:=dRSpinEdit.Value;
    X0:=OrigX0SpinEdit.Value;
    Y0:=OrigY0SpinEdit.Value;
    RoundCount:=0;
    For I:=0 To List.Count-1 Do Begin
      X:=X0+R*cos(Fi/180*Pi);
      Y:=Y0+R*Sin(Fi/180*Pi);
      If PlaceAtGridCheckBox.Checked Then Begin
        Gx:=Form1.GridGapEdit.Value*Round(X/Form1.GridGapEdit.Value);
        Gy:=Form1.GridGapEdit.Value*Round(Y/Form1.GridGapEdit.Value);
      End Else Begin
        GX:=X;GY:=Y;
      End;
      ObjName:=List[I];
      If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
        Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
        Scene.MoveObjectTo(Obj,GX,GY,'');
        Scene.UpdateSelHandles(List[I]);
      end Else
        If Scene.VCalloutList.IndexOf(ObjName)>=0 Then Begin
          Callout:=Scene.VCalloutList.Objects[Scene.VCalloutList.IndexOf(ObjName)] As TVCallout;
          Scene.MoveCalloutTo(Callout,GX,GY,'');
          Scene.UpdateSelHandles(List[I]);
        end;
       Fi:=Fi+dFi;
       If SpiralRadioButton.Checked Then R:=R+dr
       Else If Trunc(Abs(Fi)/360)>RoundCount Then Begin Inc(RoundCount); R:=R+dR;End;
    End;
  End;


  If ArrangeStylePageControl.ActivePage=PhysicsTabSheet Then Begin
  // pseudo-physics arragement

    //building gravity-acceleration list
    If Not ListBuiltDone Then Begin
      //clear current AList then rebuild it
      If AList.Count>0 Then
        For I:=AList.Count-1 DownTo 0 Do Begin
          O1:=AList.Objects[I] As TPhyzPoint;
          AList.Delete(I);
          O1.Free;
        End;
      For I:=0 To List.Count-1 Do Begin
        O1:=TPhyzPoint.Create;
        ObjName:=List[I];
        If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
          Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
          O1.X:=Obj.X;
          O1.Y:=Obj.Y;
         // O1.M:=Obj.Value;
          O1.Name:=Obj.Name;
        End Else
          If Scene.VCalloutList.IndexOf(ObjName)>=0 Then Begin
            Callout:=Scene.VCalloutList.Objects[Scene.VCalloutList.IndexOf(ObjName)] As TVCallout;
            O1.X:=Callout.X;
            O1.Y:=Callout.Y;
            O1.M:=1;
            O1.Name:=Callout.Name;
          End;
        O1.Ax:=0;
        O1.Ay:=0;
        AList.AddObject(O1.Name,O1);
      end;
      ListBuiltDone:=True;
    End;
    // Setup Gravity for each point
    If Not GravitySetDone Then Begin
      For I:=0 To AList.Count-1 Do Begin
        O1:=AList.Objects[I] As TPhyzPoint;
        ObjName:=AList[I];
        If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
          Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
          OutboundCount:=0;
          InboundCount:=0;
          If Obj.PortList.Count>0 Then
            For K:=0 To Obj.PortList.Count-1 Do Begin
              Port:=Obj.PortList.Objects[K] As TVPort;
              If Port.Connection<>'' Then Begin
                If Port.ConnectionPort='Start' Then Inc(OutboundCount);
                If Port.ConnectionPort='End' Then Inc(InboundCount);
              End;
            End;
          Case MassRadioGroup.ItemIndex Of
          0: O1.M:=Obj.Value;
          1: O1.M:=Obj.W;
          2: O1.M:=Obj.H;
          3: O1.M:=Obj.W+Obj.H; //diameter
          4: O1.M:=InboundCount; //Inbound count
          5: O1.M:=OutBoundCount; //Outbound count
          6: O1.M:=InboundCount+OutboundCount; //Total Count
          End;
        End Else
          If Scene.VCalloutList.IndexOf(ObjName)>=0 Then Begin
            Callout:=Scene.VCalloutList.Objects[Scene.VCalloutList.IndexOf(ObjName)] As TVCallout;
            O1.X:=Callout.X;
            O1.Y:=Callout.Y;
            O1.M:=1;
            O1.Name:=Callout.Name;
          End;
      End;
      GravitySetDone:=True;
    End;

    Elasticity:=ElasticityTrackBar.Position/25;
    AngVelocity:=AngVelocityTrackBar.Position/10;
    //compute acceleration for each point
    If Not PreProcessed Then Begin
      For I:=0 To AList.Count-1 Do Begin
        O1:=AList.Objects[I] As TPhyzPoint;
        O1.Ax:=0;
        O1.Ay:=0;
        // rotational acceleration
        O1.Ax:=O1.Ax+(O1.X)*AngVelocity*AngVelocity/2;
        O1.Ay:=O1.Ay+(O1.Y)*AngVelocity*AngVelocity/2;
      end;
      For I:=0 To AList.Count-1 Do Begin
        O1:=AList.Objects[I] As TPhyzPoint;
        For J:=0 To AList.Count-1 Do Begin
          O2:=AList.Objects[J] As TPhyzPoint;
          If QulonRadioButton.Checked Then Begin
            //qulon mode
            If O1.X<>O2.X Then
            O1.Ax:=O1.Ax+O1.M/((O1.X-O2.X)*(O1.X-O2.X))*Sign(O1.X-O2.X);
            If O1.Y<>O2.Y Then
            O1.Ay:=O1.Ay+O1.M/((O1.Y-O2.Y)*(O1.Y-O2.Y))*Sign(O1.Y-O2.Y);
          End Else Begin
            //gravity mode
            If (O1.X-O2.X)>0.0001 Then
            O1.Ax:=O1.Ax+0.001*O1.M/((O2.X-O1.X){*(O2.X-O1.X))*Sign(O2.X-O1.X});
            If (O1.Y-O2.Y)>0.0001 Then
            O1.Ay:=O1.Ay+0.001*O1.M/((O2.Y-O1.Y){*(O2.Y-O1.Y))*Sign(O2.Y-O1.Y});
            //ToDo: add gravity to artificial center
          End;
        end;

        If QulonRadioButton.Checked Then RubberSign:=-1
        Else RubberSign:=1;

        If Scene.VObjectList.IndexOf(AList[I])>=0 Then Begin //объект и связи
          Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(AList[I])] As TVObject;
          If Obj.PortList.Count>0 Then
            For K:=0 To Obj.PortList.Count-1 Do Begin
              Port:=Obj.PortList.Objects[K] As TVPort;
              If Port.Connection<>'' Then Begin
                Connection:=Scene.VConnectionList.Objects[Scene.VConnectionList.IndexOf(Port.Connection)] As TVConnection;
                If Connection.StartObj=O1.Name Then
                  J:=AList.IndexOf(Connection.EndObj)
                Else
                  J:=AList.IndexOf(Connection.StartObj);
                If J>=0 Then Begin
                  O2:=AList.Objects[J] As TPhyzPoint;
                  If Connection.Value<>0 Then Begin
                    O1.Ax:=O1.Ax+Elasticity*Connection.Value*(O2.X-O1.X)*RubberSign;
                    O1.Ay:=O1.Ay+Elasticity*Connection.Value*(O2.Y-O1.Y)*RubberSign;
                  end Else Begin
                    O1.Ax:=O1.Ax+Elasticity*(O2.X-O1.X)*RubberSign;
                    O1.Ay:=O1.Ay+Elasticity*(O2.Y-O1.Y)*RubberSign;
                  end;
                End;
              end;
            end;
        End;
      End;
      PreProcessed:=True;
    end;

    //двигаем собственно объекты
    T:=TimeTrackBar.Position/150;
    For I:=0 To List.Count-1 Do Begin
      ObjName:=List[I];
      O1:=AList.Objects[Alist.IndexOf(ObjName)] As TPhyzPoint;
      X:=O1.X+O1.ax*T*T;
      Y:=O1.Y+O1.ay*T*T;
      If PlaceAtGridCheckBox.Checked Then Begin
        Gx:=Form1.GridGapEdit.Value*Round(X/Form1.GridGapEdit.Value);
        Gy:=Form1.GridGapEdit.Value*Round(Y/Form1.GridGapEdit.Value);
      End Else Begin
        GX:=X;GY:=Y;
      End;

      If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
        Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
        Scene.MoveObjectTo(Obj,GX,GY,'');
        Scene.UpdateSelHandles(List[I]);
      end Else
        If Scene.VCalloutList.IndexOf(ObjName)>=0 Then Begin
          Callout:=Scene.VCalloutList.Objects[Scene.VCalloutList.IndexOf(ObjName)] As TVCallout;
          Scene.MoveCalloutTo(Callout,GX,GY,'');
          Scene.UpdateSelHandles(List[I]);
        end;
    end;
  End;
  If ArrangeStylePageControl.ActivePage=LinkTreeTabSheet Then Begin //link tree arragement
    NewShell:=Scene.ScreenShell;
    SmallShell.X1:=(NewShell.X1+NewShell.X2)/2-(NewShell.X2-NewShell.X1)*0.8/2;
    SmallShell.X2:=(NewShell.X1+NewShell.X2)/2+(NewShell.X2-NewShell.X1)*0.8/2;
    SmallShell.Y1:=(NewShell.Y1+NewShell.Y2)/2-(NewShell.Y2-NewShell.Y1)*0.8/2;
    SmallShell.Y2:=(NewShell.Y1+NewShell.Y2)/2+(NewShell.Y2-NewShell.Y1)*0.8/2;
    W:=SmallShell.X2-SmallShell.X1;
    H:=SmallShell.Y2-SmallShell.Y1;
    LayerList:=TStringList.Create;
    LayerList.AddObject('0',List);
    CurrentLayer:=List;
    //считаем Xцентра начального слоя
    If List.Count=1 Then Begin
      ObjName:=List[0];
      If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
        Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
        XCenter:=Obj.X;
        YCenter:=Obj.Y;
      End;
    End Else Begin
      XCenter:=(SmallShell.X2+SmallShell.X1)/2;
      YCenter:=(SmallShell.Y2+SmallShell.Y1)/2;
    End;
    If DirectionUpSpeedButton.Down or DirectionDownSpeedButton.Down Then Begin
      MainAxisC:=XCenter;
      SecAxisC:=YCenter;
    End Else Begin
      MainAxisC:=YCenter;
      SecAxisC:=XCenter;
    end;
    If DirectionUpSpeedButton.Down Then Begin MainAxisSign:=1;SecAxisSign:=1;End;
    If DirectionDownSpeedButton.Down Then Begin MainAxisSign:=1;SecAxisSign:=-1;End;
    If DirectionRightSpeedButton.Down Then Begin MainAxisSign:=1;SecAxisSign:=1;End;
    If DirectionLeftSpeedButton.Down Then Begin MainAxisSign:=1;SecAxisSign:=-1;End;


    For Hop:=0 To HopCountSpinEdit.Value Do Begin
      NextLayer:=TStringList.Create;
      LayerList.AddObject(IntToStr(Hop),NextLayer);
      For I:=0 To CurrentLayer.Count-1 Do Begin
        ObjName:=CurrentLayer[I];
        If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
          Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
          //тут ищем все связи текущего объекта (или не все а только вход/выход)
          //добавляем найденное, но ещё не имеющееся в пред. слоях, в NextLayer
          OutboundCount:=0;
          InboundCount:=0;
          If Obj.PortList.Count>0 Then
            For K:=0 To Obj.PortList.Count-1 Do Begin
              Port:=Obj.PortList.Objects[K] As TVPort;
              If Port.Connection<>'' Then Begin
                If Port.ConnectionPort='Start' Then Inc(OutboundCount);
                If Port.ConnectionPort='End' Then Inc(InboundCount);
                N:=Scene.VConnectionList.IndexOf(Port.Connection);
                If N>=0 Then Begin
                  Connection:=Scene.VConnectionList.Objects[N] As TVConnection;
                  If (ObjName=Connection.EndObj) And Not (ConsiderInBoundSpeedButton.Down Or ConsiderBothSpeedButton.Down) Then Continue;
                  If (ObjName=Connection.StartObj) And Not (ConsiderOutBoundSpeedButton.Down Or ConsiderBothSpeedButton.Down) Then Continue;
                  If ObjName=Connection.EndObj Then Obj1Name:=Connection.StartObj;
                  If ObjName=Connection.StartObj Then Obj1Name:=Connection.EndObj;
                  N:=Scene.VObjectList.IndexOf(Obj1Name);
                  If N>=0 Then Begin
                    Obj1:=Scene.VObjectList.Objects[N] As TVObject;
                    //If Obj1 not in previous layers
                    PlacedEarlier:=False;
                    For J:=0 To LayerList.Count-2 Do Begin
                      TestLayer:=LayerList.Objects[J] As TStringList;
                      If TestLayer.IndexOf(Obj1.Name)>=0 Then Begin
                        PlacedEarlier:=True;
                        Break;
                      end;
                    end;
                    If Not PlacedEarlier Then
                      If NextLayer.IndexOf(Obj1.Name)<0 Then NextLayer.AddObject(Obj1.Name,Obj1);
                  end;
                end;
              End;
            End;
          //расставляем текущее Obj.X:=Xцентра-dW*CurrentList.Count/2+i*dW
          If FractalCheckBox.Checked Then dW:=MainAxisSign*dWFloatSpinEdit.Value/(Power(dWdivFloatSpinEdit.Value,Hop))
          Else dw:=MainAxisSign*dWFloatSpinEdit.Value;
          dH:=dHFloatSpinEdit.Value;
          If Hop>0 Then MainAxis:=MainAxisC-dW*CurrentLayer.Count/2+I*dW Else MainAxis:=MainAxisC;
          If FractalCheckBox.Checked Then Begin
            dH:=0;
            ddh:=dHFloatSpinEdit.Value;
            For K:=1 To Hop Do Begin
              dH:=dH+ddH;
              ddh:=ddh*dHdivFloatSpinEdit.Value;
            end;
            If Hop>0 Then SecAxis:=SecAxisC+SecAxisSign*dH Else SecAxis:=SecAxisC;
          End Else SecAxis:=SecAxisC+SecAxisSign*dH*Hop;
          If DirectionUpSpeedButton.Down or DirectionDownSpeedButton.Down Then Begin
              X:=MainAxis;
              Y:=SecAxis;
            End Else Begin
              Y:=MainAxis;
              X:=SecAxis;
            end;

          If PlaceAtGridCheckBox.Checked Then Begin
            Gx:=Form1.GridGapEdit.Value*Round(X/Form1.GridGapEdit.Value);
            Gy:=Form1.GridGapEdit.Value*Round(Y/Form1.GridGapEdit.Value);
          End Else Begin
            GX:=X;GY:=Y;
          End;
          Scene.MoveObjectTo(Obj,GX,GY,'');
          Scene.UpdateSelHandles(Obj.Name);
        End;
      End;
      CurrentLayer:=NextLayer;
    End;
    //освободить все слои
    For I:=LayerList.Count-1 DownTo 1 Do Begin
      CurrentLayer:=LayerList.Objects[I] As TStringList;
      CurrentLayer.Clear;
      CurrentLayer.Free;
    end;
    LayerList.Free;
  End;


  If ArrangeStylePageControl.ActivePage=LinkCircleTabSheet Then Begin //link circle arragement
     NewShell:=Scene.ScreenShell;
     SmallShell.X1:=(NewShell.X1+NewShell.X2)/2-(NewShell.X2-NewShell.X1)*0.8/2;
     SmallShell.X2:=(NewShell.X1+NewShell.X2)/2+(NewShell.X2-NewShell.X1)*0.8/2;
     SmallShell.Y1:=(NewShell.Y1+NewShell.Y2)/2-(NewShell.Y2-NewShell.Y1)*0.8/2;
     SmallShell.Y2:=(NewShell.Y1+NewShell.Y2)/2+(NewShell.Y2-NewShell.Y1)*0.8/2;
     W:=0;
     H:=0;
     LayerList:=TStringList.Create;
     LayerList.AddObject('0',List);
     CurrentLayer:=List;
     //считаем Xцентра начального слоя
     If List.Count=1 Then Begin
       ObjName:=List[0];
       If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
         Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
         XCenter:=Obj.X;
         YCenter:=Obj.Y;
       End;
     End Else Begin
       XCenter:=(SmallShell.X2+SmallShell.X1)/2;
       YCenter:=(SmallShell.Y2+SmallShell.Y1)/2;
     End;

     For Hop:=0 To CHopCountSpinEdit.Value Do Begin
       NextLayer:=TStringList.Create;
       LayerList.AddObject(IntToStr(Hop),NextLayer);
       For I:=0 To CurrentLayer.Count-1 Do Begin
         ObjName:=CurrentLayer[I];
         If Scene.VObjectList.IndexOf(ObjName)>=0 Then Begin
           Obj:=Scene.VObjectList.Objects[Scene.VObjectList.IndexOf(ObjName)] As TVObject;
           //тут ищем все связи текущего объекта (или не все а только вход/выход)
           //добавляем найденное, но ещё не имеющееся в пред. слоях, в NextLayer
           OutboundCount:=0;
           InboundCount:=0;
           If Obj.PortList.Count>0 Then
             For K:=0 To Obj.PortList.Count-1 Do Begin
               Port:=Obj.PortList.Objects[K] As TVPort;
               If Port.Connection<>'' Then Begin
                 If Port.ConnectionPort='Start' Then Inc(OutboundCount);
                 If Port.ConnectionPort='End' Then Inc(InboundCount);
                 N:=Scene.VConnectionList.IndexOf(Port.Connection);
                 If N>=0 Then Begin
                   Connection:=Scene.VConnectionList.Objects[N] As TVConnection;
                   If (ObjName=Connection.EndObj) And Not (ConsiderInBoundSpeedButton1.Down Or ConsiderBothSpeedButton1.Down) Then Continue;
                   If (ObjName=Connection.StartObj) And Not (ConsiderOutBoundSpeedButton1.Down Or ConsiderBothSpeedButton1.Down) Then Continue;
                   If ObjName=Connection.EndObj Then Obj1Name:=Connection.StartObj;
                   If ObjName=Connection.StartObj Then Obj1Name:=Connection.EndObj;
                   N:=Scene.VObjectList.IndexOf(Obj1Name);
                   If N>=0 Then Begin
                     Obj1:=Scene.VObjectList.Objects[N] As TVObject;
                     //If Obj1 not in previous layers
                     PlacedEarlier:=False;
                     For J:=0 To LayerList.Count-2 Do Begin
                       TestLayer:=LayerList.Objects[J] As TStringList;
                       If TestLayer.IndexOf(Obj1.Name)>=0 Then Begin
                         PlacedEarlier:=True;
                         Break;
                       end;
                     end;
                     If Not PlacedEarlier Then
                       If NextLayer.IndexOf(Obj1.Name)<0 Then NextLayer.AddObject(Obj1.Name,Obj1);
                   end;
                 end;
               End;
             End;
           //расставляем текущее Obj.X:=Xцентра-dW*CurrentList.Count/2+i*dW
           If CFractalCheckBox.Checked Then dFi:=(Fi2FloatSpinEdit.Value-Fi1FloatSpinEdit.Value)/CurrentLayer.Count/(Power(dFidivFloatSpinEdit.Value,Hop))
           Else dFi:=(Fi2FloatSpinEdit.Value-Fi1FloatSpinEdit.Value)/CurrentLayer.Count;

           {If Hop>0 Then }MainAxis:=Fi1FloatSpinEdit.Value+dFi*I;
           If CFractalCheckBox.Checked Then Begin
             dH:=0;
             ddh:=dHFloatSpinEdit.Value;
             For K:=1 To Hop Do Begin
               dH:=dH+ddH;
               ddh:=ddh*dHdivFloatSpinEdit.Value;
             end;
             If Hop>0 Then H:=H+dH;
           End Else dH:=dRFloatSpinEdit.Value*Hop;
           X:=XCenter+dH*Cos(Pi*MainAxis/180);
           Y:=YCenter+dH*Sin(Pi*MainAxis/180);
           If PlaceAtGridCheckBox.Checked Then Begin
             Gx:=Form1.GridGapEdit.Value*Round(X/Form1.GridGapEdit.Value);
             Gy:=Form1.GridGapEdit.Value*Round(Y/Form1.GridGapEdit.Value);
           End Else Begin
             GX:=X;GY:=Y;
           End;
           Scene.MoveObjectTo(Obj,GX,GY,'');
           Scene.UpdateSelHandles(Obj.Name);
         End;
       End;
       CurrentLayer:=NextLayer;
     End;
     //освободить все слои
     For I:=LayerList.Count-1 DownTo 1 Do Begin
       CurrentLayer:=LayerList.Objects[I] As TStringList;
       CurrentLayer.Clear;
       CurrentLayer.Free;
     end;

     LayerList.Free;
  End;

  List.Free;
  Dispatcher.ExecCommand('self::CAD_UpdateCoordBase');
End;

procedure TArrangeForm.RebuildParticleListSpeedButtonClick(Sender: TObject);
begin
  ListBuiltDone:=False;
  GravitySetDone:=False;
  PreProcessed:=False;
end;

procedure TArrangeForm.TimeTrackBarChange(Sender: TObject);
begin
  If (AutoArrangeSpeedButton.Down) Then ProcessPlaceBitBtnClick(Sender);
end;

procedure TArrangeForm.ElasticityTrackBarChange(Sender: TObject);
begin
  PreProcessed:=False;
  If (AutoArrangeSpeedButton.Down) Then ProcessPlaceBitBtnClick(Sender);
end;

procedure TArrangeForm.FormCreate(Sender: TObject);
begin
  AList:=TStringList.Create;
end;

Procedure TArrangeForm.CancelBitBtnClick(Sender: TObject);
Begin
  Hide;
end;

Procedure TArrangeForm.FormShow(Sender: TObject);
Begin
  ApplyToCheckGroup.Checked[0]:=True;
end;

procedure TArrangeForm.MassRadioGroupSelectionChanged(Sender: TObject);
begin
  GravitySetDone:=False;
  PreProcessed:=False;
  If AutoArrangeSpeedButton.Down Then
    ProcessPlaceBitBtnClick(Sender);
end;

Procedure TArrangeForm.PeekOriginSpeedButton1Click(Sender: TObject);
Begin
  GettingPoint:=True;
  OrigX0SpinEdit.Hide;
  OrigY0SpinEdit.Hide;
  PeekPointLabel1.Show;
  Repeat Application.ProcessMessages Until Not GettingPoint;
  PeekPointLabel1.Hide;
  OrigX0SpinEdit.Show;
  OrigY0SpinEdit.Show;
  OrigX0SpinEdit.Value:=CurrentXw;
  OrigY0SpinEdit.Value:=CurrentYw;
  ProcessPlaceBitBtnClick(Sender);
end;

Procedure TArrangeForm.PeekOriginSpeedButtonClick(Sender: TObject);
Begin
  GettingPoint:=True;
  OrigXSpinEdit.Hide;
  OrigYSpinEdit.Hide;
  PeekPointLabel.Show;
  Repeat Application.ProcessMessages Until Not GettingPoint;
  PeekPointLabel.Hide;
  OrigXSpinEdit.Show;
  OrigYSpinEdit.Show;
  OrigXSpinEdit.Value:=CurrentXw;
  OrigYSpinEdit.Value:=CurrentYw;
  ProcessPlaceBitBtnClick(Sender);
end;

Initialization
  {$I ArrangeDialog.lrs}

End.

