unit GraphS;

interface

(*
  5.08.99 - добавление на график экспериментальных точек
*)

uses Windows, Forms, SysUtils, SharedData, MiscRoutines, VertList,
  // Двухмерная графика
  TeeGraph,
  // Трехмерная графика
  U3D, OpenGL, GLWindow, GLScene, Controller, StdFont;

type
  TGraphAction=(
    acBeginGraph,
    acBeginSurface,
    acEndGraph,
    acEndSurface,
    acSetNames,
    acAddVertex,
    acImport,
    acReset,
    acShow,
    acHide,
    acArrange
  );

type
  PSourceNames=^TSourceNames;
  TSourceNames=packed record
    FirstName,
    SecondName,
    ResponseName : PChar;
  end;

// =======================================================================
// Интерфейс на сервер графики.
// =======================================================================

type
  pIGraphServer = ^IGraphServer;
  IGraphServer = object
  public
    procedure    Vertex2D(x, y: double);virtual;stdcall;abstract;
    procedure    Vertex3D(x, y, z: double);virtual;stdcall;abstract;
    procedure    BeginGraph(Count,Reserved:longint);virtual;stdcall;abstract;
    procedure    BeginSurface(SizeX,SizeY:longint);virtual;stdcall;abstract;
    procedure    SetNames(GNames,SNames:PSourceNames);virtual;stdcall;abstract;
    procedure    EndGraph(Count,Reserved:longint);virtual;stdcall;abstract;
    procedure    EndSurface(SizeX,SizeY:longint);virtual;stdcall;abstract;
    procedure    Show(ShowWindow:Boolean);virtual;stdcall;abstract;
    procedure    Arrange;virtual;stdcall;abstract;
    procedure    Reset;virtual;stdcall;abstract;
    procedure    SetupChanged;virtual;stdcall;abstract;
    function     CanDo(Action:TGraphAction):Boolean;virtual;stdcall;abstract;
    procedure    DisplayChange(cx,cy,bpp:integer);virtual;stdcall;abstract;
    // 5.08.99
    procedure    BeginPoints2D(Count:longint);virtual;stdcall;abstract;
    procedure    BeginPoints3D(Count:longint);virtual;stdcall;abstract;
    procedure    EndPoints2D(Count:longint);virtual;stdcall;abstract;
    procedure    EndPoints3D(Count:longint);virtual;stdcall;abstract;
  end;

procedure CreateGraphServer(var pIServer:pIGraphServer);stdcall;
procedure DestroyGraphServer(var pIServer:pIGraphServer);stdcall;

implementation

uses Setup;

type
  PGraphServer = ^TGraphServer;
  TGraphServer = object(IGraphServer)
  public
    // ===================================================================
    // ВНИМАНИЕ !!! Эти методы нельзя переставлять местами.
    procedure    Vertex2D(x, y: double);virtual;stdcall;
    procedure    Vertex3D(x, y, z: double);virtual;stdcall;
    procedure    BeginGraph(Count,Reserved:longint);virtual;stdcall;
    procedure    BeginSurface(SizeX,SizeY:longint);virtual;stdcall;
    procedure    SetNames(GNames,SNames:PSourceNames);virtual;stdcall;
    procedure    EndGraph(Count,Reserved:longint);virtual;stdcall;
    procedure    EndSurface(SizeX,SizeY:longint);virtual;stdcall;
    procedure    Show(ShowWindow:Boolean);virtual;stdcall;
    procedure    Arrange;virtual;stdcall;
    procedure    Reset;virtual;stdcall;
    procedure    SetupChanged;virtual;stdcall;
    function     CanDo(Action:TGraphAction):Boolean;virtual;stdcall;
    procedure    DisplayChange(cx,cy,bpp:integer);virtual;stdcall;
    //           5.08.99
    procedure    BeginPoints2D(Count:longint);virtual;stdcall;
    procedure    BeginPoints3D(Count:longint);virtual;stdcall;
    procedure    EndPoints2D(Count:longint);virtual;stdcall;
    procedure    EndPoints3D(Count:longint);virtual;stdcall;
    // ===================================================================
    constructor  Create;
    procedure    Finalize;
  private
    TeeData,
    SurfData     : Boolean;
    VList2       : TPlainVertices;
    PList2       : TPlainVertices;
    List2        : PPlainVertices;
    TeeWinPresent: Boolean;
    TeeWindow    : TGraphForm;
    K,L          : integer;
    nPoints      : integer;
    VList3       : TSurfaceVertices;
    PList3       : TSurfaceVertices;
    List3        : PSurfaceVertices;
    OpenGLWindow : TOpenGLWindow;
    OGLWinPresent: Boolean;
    GraphNames   : TSourceNames;
    SurfaceNames : TSourceNames;
    procedure    NotifyTeeClose(Sender:TObject);
    procedure    NotifyGLClose(Sender:TObject);
  end;

constructor TGraphServer.Create;
begin
  VList2.Create;
  VList2.Clear;
  VList3.Create;
  VList3.Clear;
  PList2.Create;
  PList2.Clear;
  PList3.Create;
  PList3.Clear;
  OpenGLWindow:=nil;
  TeeWindow:=nil;
  OGLWinPresent:=False;
  TeeWinPresent:=False;
  TeeData:=False;
  SurfData:=False;
  GraphNames.FirstName:='Фактор';
  GraphNames.SecondName:='';
  GraphNames.ResponseName:='Отклик системы';
  SurfaceNames.FirstName:='Фактор';
  SurfaceNames.SecondName:='Фактор';
  SurfaceNames.ResponseName:='Отклик системы';
end;

procedure TGraphServer.Finalize;
begin
  if(TeeWinPresent) then
    TeeWindow.Close;
  if(OGLWinPresent) then
    OpenGLWindow.Close;
  VList2.Destroy;
  VList3.Destroy;
  PList2.Destroy;
  PList3.Destroy;
  List2:=nil;
  List3:=nil;
end;

procedure TGraphServer.Vertex2D;
begin
  Assert(List2<>nil);
  List2^.Add(x,y);
end;

procedure TGraphServer.Vertex3D;
begin
  Assert(List3<>nil);
  List3^.Add(x,z,y);
end;

procedure TGraphServer.BeginGraph;
begin
  Reset;
  K:=Count;
  L:=Reserved;
  List2:=@VList2;
  List3:=nil;
end;

procedure TGraphServer.BeginSurface;
begin
  Reset;
  K:=SizeX;
  L:=SizeY;
  List2:=nil;
  List3:=@VList3;
end;

procedure TGraphServer.BeginPoints2D;
begin
  nPoints:=Count;
  List2:=@PList2;
  List3:=nil;
  List2.Clear;
end;

procedure TGraphServer.BeginPoints3D;
begin
  nPoints:=Count;
  List2:=nil;
  List3:=@PList3;
  List2.Clear;
end;

procedure TGraphServer.SetNames;
begin
  if(GNames<>nil) then
  begin
    GraphNames.FirstName:=GNames^.FirstName;
    GraphNames.SecondName:=GNames^.SecondName;
    GraphNames.ResponseName:=GNames^.ResponseName;
  end;
  if(SNames<>nil) then
  begin
    SurfaceNames.FirstName:=SNames^.FirstName;
    SurfaceNames.SecondName:=SNames^.SecondName;
    SurfaceNames.ResponseName:=SNames^.ResponseName;
  end;
end;

procedure TGraphServer.EndGraph;
begin
  TeeData:=True;
end;

procedure TGraphServer.EndSurface;
begin
  SurfData:=True;
end;

procedure TGraphServer.EndPoints2D;
begin
  Assert(List2<>nil);
  Assert(List2^.Count=nPoints);
  Assert(List2^.Count=Count);
  List2:=nil;
  List3:=nil;
end;

procedure TGraphServer.EndPoints3D;
begin
  Assert(List3<>nil);
  Assert(List3^.Count=nPoints);
  Assert(List3^.Count=Count);
  List2:=nil;
  List3:=nil;
end;

procedure TGraphServer.Show;
var
  i,j,count : integer;
  n,x : vector_t;
begin
  if(ShowWindow) then
  begin
    if(TeeData) then
    begin
      if(not TeeWinPresent) then
      begin
        TeeWindow:=TGraphForm.Create(Application.MainForm);
        TeeWindow.NotifyTeeClose:=NotifyTeeClose;
        TeeWinPresent:=True;
      end;
      TeeWindow.Series.XValues.ClearList;
      TeeWindow.Series.YValues.ClearList;
      TeeWindow.Points.XValues.ClearList;
      TeeWindow.Points.YValues.ClearList;
      for i:=0 to VList2.Count-1 do
      begin
        TeeWindow.Series.XValues.AddChartValue(VList2.Items[i].x);
        TeeWindow.Series.YValues.AddChartValue(VList2.Items[i].y);
      end;
      if(PList2.Count>0) then
      begin
        PList2.Sort; // BUGBUG in TeeChart - или это фича ?
        for i:=0 to PList2.Count-1 do
        begin
          TeeWindow.Points.XValues.AddChartValue(PList2.Items[i].x);
          TeeWindow.Points.YValues.AddChartValue(PList2.Items[i].y);
        end;
        TeeWindow.Points.XValues.RecalcMinMax;
        TeeWindow.Points.YValues.RecalcMinMax;
      end;
      TeeWindow.Series.XValues.RecalcMinMax;
      TeeWindow.Series.YValues.RecalcMinMax;
      TeeWindow.Chart.BottomAxis.Title.Caption:=GraphNames.FirstName;
      TeeWindow.Chart.LeftAxis.Title.Caption:=GraphNames.ResponseName;
    end;
    if(SurfData) then
    begin
      if(not OGLWinPresent) then
      begin
        OpenGLWindow:=TOpenGLWindow.Create(Application.MainForm);
        OpenGLWindow.NotifyGLClose:=NotifyGLClose;
        OGLWinPresent:=True;
      end
      else
      begin
        OpenGLWindow.Scene.SurfaceCreated:=False;
        OpenGLWindow.Scene.Surface.Destroy;
      end;
      OpenGLWindow.Scene.Surface.Create(K,L);
      count:=0;
      for i:=1 to K do
      begin
        for j:=1 to L do
        begin
          OpenGLWindow.Scene.Surface.Vertex(VList3.Pointers[count],nil,nil);
          count:=count+1;
        end;
      end;
      vtSet(n,-0.5,0,-0.5);
      vtSet(x,0.5,1,0.5);
      OpenGLWindow.Scene.Surface.SetBoundingBox(n,x);
      // Устанавливаем закраску
      SetupChanged;
      OpenGLWindow.Scene.SurfaceCreated:=True;
      {
      if(AppSetup.GLFlags.UseVR) then
        OpenGLWindow.Scene.KeyController.ControlMode:=cmVirtualReality
      else
        OpenGLWindow.Scene.KeyController.ControlMode:=cmSceneControl;}
    end;
  end
  else // not ShowWindow
  begin
    if(TeeWinPresent) then
    begin
      TeeWindow.WindowState:=wsMinimized;
      TeeWinPresent:=False;
      TeeWindow.Close;
    end;
    if(OGLWinPresent) then
    begin
      OpenGLWindow.WindowState:=wsMinimized;
      OGLWinPresent:=False;
      OpenGLWindow.Close;
    end;
  end;
end;

procedure TGraphServer.NotifyGLClose;
begin
  OGLWinPresent:=False;
  OpenGLWindow:=nil;
end;

procedure TGraphServer.NotifyTeeClose;
begin
  TeeWinPresent:=False;
  TeeWindow:=nil;
end;

procedure TGraphServer.Arrange;
begin
  exit;
  // #warning not implemented
end;

procedure TGraphServer.Reset;
begin
  if(OGLWinPresent) then
    OpenGLWindow.Close;
  if(TeeWinPresent) then
    TeeWindow.Close;
  VList2.Clear;
  PList2.Clear;
  TeeData:=False;
  VList3.Clear;
  PList3.Clear;
  SurfData:=False;
end;

procedure TGraphServer.SetupChanged;
var
  count,i,j : integer;
  Origin,Space,Offset,y : u3dval;
  rgb,rgb1,rgb2:rgb_t;
  min,max : vector_t;
begin
  if(OGLWinPresent) then
  begin
    count:=0;
    case AppSetup.GLFlags.SurfScheme of
    scSolid:
      begin
        Color2RGB(AppSetup.GLFlags.SurfColor1,rgb);
        for i:=1 to K do
        begin
          for j:=1 to L do
          begin
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].r:=rgb.r;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].g:=rgb.g;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].b:=rgb.b;
            count:=count+1;
          end;
        end;
      end;
    scGradient:
      begin
        Color2RGB(AppSetup.GLFlags.SurfColor1,rgb1);
        Color2RGB(AppSetup.GLFlags.SurfColor2,rgb2);
        surfGetBBox(@OpenGLWindow.Scene.Surface.Surf,min,max);
        Origin:=min.y;
        Space:=max.y-min.y;
        if(Space=0.0) then Space:=0.01;
        for i:=1 to K do
        begin
          for j:=1 to L do
          begin
            y:=OpenGLWindow.Scene.Surface.Surf.pVertices^[count].y;
            // Линейная интерполяция
            Offset:=(y-Origin)/Space; // 0 <= Offset <= 1
            rgb.r:=rgb1.r+(rgb2.r-rgb1.r)*Offset;
            rgb.g:=rgb1.g+(rgb2.g-rgb1.g)*Offset;
            rgb.b:=rgb1.b+(rgb2.b-rgb1.b)*Offset;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].r:=rgb.r;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].g:=rgb.g;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].b:=rgb.b;
            count:=count+1;
          end;
        end;
      end;
    scRandom:
      begin
        for i:=1 to K do
        begin
          for j:=1 to L do
          begin
            rgb.r:=Random(256)/256;
            rgb.g:=Random(256)/256;
            rgb.b:=Random(256)/256;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].r:=rgb.r;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].g:=rgb.g;
            OpenGLWindow.Scene.Surface.Surf.pColors^[count].b:=rgb.b;
            count:=count+1;
          end;
        end;
      end;
    end;
    OpenGLWindow.SetupChanged;
  end;
  if(TeeWinPresent) then
    TeeWindow.SetupChanged;
end;

function TGraphServer.CanDo;
begin
  Result:=False;
  case Action of
    acBeginGraph:
    begin
      Result:=True;
    end;
    acBeginSurface:
    begin
      Result:=True;
    end;
    acSetNames:
    begin
      Result:=True;
    end;
    acEndGraph:
    begin
      Result:=True;
    end;
    acEndSurface:
    begin
      Result:=True;
    end;
    acAddVertex:
    begin
      Result:=True;
    end;
    acImport:
    begin
      Result:=False;
    end;
    acReset:
    begin
      Result:=True;
    end;
    acShow:
    begin
      Result:=(TeeData and (not TeeWinPresent))
        or (SurfData and (not OGLWinPresent));
    end;
    acHide:
    begin
      Result:= TeeWinPresent or OGLWinPresent;
    end;
    acArrange:
    begin
      Result:=False;
    end;
    else Assert(False);
  end;
end;

procedure TGraphServer.DisplayChange(cx,cy,bpp:integer);
begin
  // В текущей версии это заглушка. Пробовал пересоздавать окно OpenGL,
  // но в NT получаю сообщение: "Сетевой запрос не поддерживается" (бред:)
end;

// =======================================================================

procedure CreateGraphServer;
var
  RealGS : PGraphServer;
begin
  New(RealGS,Create);
  pIServer:=pIGraphServer(RealGS);
end;

procedure DestroyGraphServer;
var
  RealGS : PGraphServer;
begin
  RealGS:=PGraphServer(pIServer);
  RealGS.Finalize;
  Dispose(RealGS);
end;

end.

