{*********************************************}
{  TeeChart Charting Library for FireMonkey   }
{  Copyright (c) 2012-2020 by Steema Software }
{  All Rights Reserved                        }
{*********************************************}
unit FMXTee.Editor.Aspect;
{$I TeeDefs.inc}

interface

uses
  System.SysUtils, System.Types, System.Classes, System.Variants, FMX.Types,
  System.Generics.Collections,
  FMX.Controls, FMX.Forms,
  {$IFNDEF UPTOXE3}
  FMX.StdCtrls, FMX.Controls3D,
  {$ENDIF}
  FMX.Edit, FMX.ListBox, FMX.Dialogs, FMX.Layouts, FMX.TabControl,
  {$IFDEF D21}
  FMX.ComboTrackBar, FMX.SpinBox,
  {$ENDIF}
  FMXTee.Procs, FMXTee.Canvas, FMXTee.Chart.GalleryPanel;

type
  TFormTee3D = class(TForm)
    PageControl1: TTabControl;
    Tab3D: TTabItem;
    L13: TLabel;
    L4: TLabel;
    L35: TLabel;
    L36: TLabel;
    LZoom: TLabel;
    LRotation: TLabel;
    LElevation: TLabel;
    Label1: TLabel;
    LHOffset: TLabel;
    Label3: TLabel;
    LVOffset: TLabel;
    Label2: TLabel;
    LPerspec: TLabel;
    LabelOrtho: TLabel;
    LTextSize: TLabel;
    Label5: TLabel;
    CBView3d: TCheckBox;
    CBOrthogonal: TCheckBox;
    SBZoom: TTrackBar;
    UDTextSize: TSpinBox;
    CBZoomText: TComboBox;
    TabViews: TTabItem;
    TabRender: TTabItem;
    Splitter1: TSplitter;
    LBRender: TListBox;
    PanelRender: TPanel;
    PanelGDI: TPanel;
    CBBuffered: TCheckBox;
    SBRotation: TTrackBar;
    SBElevation: TTrackBar;
    SBHorizOffset: TTrackBar;
    SBVertOffset: TTrackBar;
    SBPerspec: TTrackBar;
    CBClipPoints: TCheckBox;
    UDOrthoAngle: TSpinBox;
    CBGPUSurface: TCheckBox;
    CBClipRect: TCheckBox;
    CBQuality: TComboBox;
    Label4: TLabel;
    ListBoxItem4: TListBoxItem;
    ListBoxItem5: TListBoxItem;
    ListBoxItem6: TListBoxItem;
    Label6: TLabel;
    EScale: TEdit;
    CBBlending: TCheckBox;
    Percent3D: TComboTrackBar;
    Views: TChartGalleryPanel;
    LDensity: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure PageControl1Change(Sender: TObject);
    procedure SBZoomChange(Sender: TObject);
    procedure SBRotationChange(Sender: TObject);
    procedure SBElevationChange(Sender: TObject);
    procedure SBPerspecChange(Sender: TObject);
    procedure CBZoomTextChange(Sender: TObject);
    procedure CBView3dChange(Sender: TObject);
    procedure CBOrthogonalChange(Sender: TObject);
    procedure CBClipPointsChange(Sender: TObject);
    procedure UDOrthoAngleChange(Sender: TObject);
    procedure SBHorizOffsetChange(Sender: TObject);
    procedure SBVertOffsetChange(Sender: TObject);
    procedure CBBufferedChange(Sender: TObject);
    procedure UDTextSizeChange(Sender: TObject);
    procedure LBRenderChange(Sender: TObject);
    procedure CBBlendingChange(Sender: TObject);
    procedure Percent3DChangeTracking(Sender: TObject);
    procedure ViewsChangeChart(Sender: TObject);
  private
   { Private declarations }
    Panel : TCustomTeePanel;
    IDepth : Single;
    CreatingViews : Boolean;

    procedure AddRenders;
    function AspectFloat(const Value:Single):String;
    procedure CreateViewsGallery;
    procedure EnableControls;
    procedure RefreshPanel;
    procedure Select3DPanel(const APanel:TCustomTeePanel);
    procedure SetGallery3DPanel(const APanel: TCustomTeePanel);
    procedure SetSpinRepeat(const Spin: TSpinBox);
    procedure SetView3D(const AView:TView3DOptions);
    procedure UDOrthoAngleApplyStyleLookup(Sender: TObject);
  public
   { Public declarations }
    class function CreateEditor(const AOwner:TComponent; const APanel:TCustomTeePanel):TForm;
  end;

implementation

{$R *.fmx}

uses
  System.Math, FMXTee.Engine, FMXTee.Chart, FMXTee.Constants, FMX.Types3D,
  {$IFDEF D19}
  FMX.Graphics,
  {$ENDIF}
  {$IFDEF ANDROID}
  AndroidApi.JNI.Util, FMX.Helpers.Android,
  {$IFDEF D21}
  Androidapi.Helpers,
  {$ENDIF}
  {$IFDEF D24}
  Androidapi.JNI.GraphicsContentViewText,
  {$ENDIF}
  {$ENDIF}
  FMXTee.Editor.Stroke;

procedure TFormTee3D.FormCreate(Sender: TObject);
begin
  PageControl1.ActiveTab:=Tab3D;

  {$IFDEF TEELITEEMB}
  TabRender.Visible:=False;
  {$ENDIF}

  UDTextSize.OnApplyStyleLookup:=UDOrthoAngleApplyStyleLookup;
  UDOrthoAngle.OnApplyStyleLookup:=UDOrthoAngleApplyStyleLookup;
end;

procedure TFormTee3D.PageControl1Change(Sender: TObject);
begin
  if not Assigned(Panel) then
     Exit;

  if PageControl1.ActiveTab=TabViews then
  begin
    if Views.Charts.Count=0 then
       CreateViewsGallery;

    Select3DPanel(Panel);
  end
  else
  if PageControl1.ActiveTab=Tab3D then
     RefreshPanel;
end;

procedure TFormTee3D.Percent3DChangeTracking(Sender: TObject);
begin
  if Panel<>nil then
  begin
    TCustomAxisPanel(Panel).Chart3DPercent:=Round(Percent3D.Value);

    if Panel.Parent is TControl3D then
       TControl3D(Panel.Parent).Depth:=IDepth*Percent3D.Value*0.01;
  end;
end;

procedure TFormTee3D.Select3DPanel(const APanel:TCustomTeePanel);
var t,tmp : Integer;
    tmpC  : TCustomChart;
begin
  tmp:=-1;

  with Views do
  begin
    Charts[0].View3DOptions:=APanel.View3DOptions;

    for t:=0 to Charts.Count-1 do
    begin
      tmpC:=Charts[t];

      with tmpC do
      if (View3D=APanel.View3D) and
         (View3DOptions.Orthogonal=APanel.View3DOptions.Orthogonal) and
         (View3DOptions.OrthoAngle=APanel.View3DOptions.OrthoAngle) and
         (View3DOptions.Rotation=APanel.View3DOptions.Rotation) and
         (View3DOptions.Elevation=APanel.View3DOptions.Elevation) then
      begin
        tmp:=t;
        break;
      end;
    end;

    if tmp=-1 then SelectChart(nil)
              else SelectChart(Charts[tmp]);
  end;
end;

procedure TFormTee3D.SetGallery3DPanel(const APanel:TCustomTeePanel);
begin
  with Views do
  if SelectedChart<>nil then
  with SelectedChart do
  begin
    APanel.View3D:=View3D;
    APanel.View3DOptions.Orthogonal:=View3DOptions.Orthogonal;
    APanel.View3DOptions.OrthoAngle:=View3DOptions.OrthoAngle;
    APanel.View3DOptions.Rotation:=View3DOptions.Rotation;
    APanel.View3DOptions.Elevation:=View3DOptions.Elevation;
  end;
end;

procedure TFormTee3D.CBView3dChange(Sender: TObject);
begin
  Panel.View3D:=CBView3d.IsChecked;

  EnableControls;
end;

function TFormTee3D.AspectFloat(const Value:Single):String;
begin
  result:=FormatFloat('0.##',Value);
end;

procedure TFormTee3D.SBHorizOffsetChange(Sender: TObject);
begin
  Panel.View3DOptions.HorizOffsetFloat:=SBHorizOffset.Value;

  if Panel.Parent is TControl3D then
     TControl3D(Panel.Parent).Position.X:=SBHorizOffset.Value*0.01;

  LHOffset.Text:=AspectFloat(SBHorizOffset.Value);
end;

procedure TFormTee3D.UDOrthoAngleApplyStyleLookup(Sender: TObject);
begin
  SetSpinRepeat(TSpinBox(Sender));
end;

procedure TFormTee3D.UDOrthoAngleChange(Sender: TObject);
begin
  if Assigned(Panel) then
     Panel.View3DOptions.OrthoAngle:=Round(UDOrthoAngle.Value);
end;

procedure TFormTee3D.UDTextSizeChange(Sender: TObject);
begin
  if Assigned(Panel) then
  begin
    Panel.View3DOptions.FontZoom:=Max(1,Round(UDTextSize.Value));

    if Panel.Parent is TControl3D then
       Panel.Invalidate;
  end;
end;

procedure TFormTee3D.ViewsChangeChart(Sender: TObject);
begin
  if not CreatingViews then
     SetGallery3DPanel(Panel);
end;

procedure TFormTee3D.SetSpinRepeat(const Spin: TSpinBox);
begin
  TStrokeEditor.SetRepeatClick(Spin,True);
end;

procedure TFormTee3D.CBOrthogonalChange(Sender: TObject);
begin
  Panel.View3DOptions.Orthogonal:=CBOrthogonal.IsChecked;
  EnableControls;
end;

procedure TFormTee3D.SBZoomChange(Sender: TObject);
begin
  Panel.View3DOptions.ZoomFloat:=SBZoom.Value;
  LZoom.Text:=AspectFloat(SBZoom.Value)+'%';  // Do not localize
end;

procedure TFormTee3D.SBRotationChange(Sender: TObject);
begin
  Panel.View3DOptions.RotationFloat:=SBRotation.Value;

  if Panel.Parent is TControl3D then
     TControl3D(Panel.Parent).RotationAngle.Y:=SBRotation.Value;

  LRotation.Text:=AspectFloat(SBRotation.Value);
end;

procedure TFormTee3D.SBElevationChange(Sender: TObject);
begin
  Panel.View3DOptions.ElevationFloat:=SBElevation.Value;

  if Panel.Parent is TControl3D then
     TControl3D(Panel.Parent).RotationAngle.X:=SBElevation.Value;

  LElevation.Text:=AspectFloat(SBElevation.Value);
end;

procedure TFormTee3D.SBVertOffsetChange(Sender: TObject);
begin
  Panel.View3DOptions.VertOffsetFloat:=SBVertOffset.Value;

  if Panel.Parent is TControl3D then
     TControl3D(Panel.Parent).Position.Y:=SBVertOffset.Value*0.01;

  LVOffset.Text:=AspectFloat(SBVertOffset.Value);
end;

procedure TFormTee3D.SBPerspecChange(Sender: TObject);
begin
  Panel.View3DOptions.Perspective:=Round(SBPerspec.Value);
  LPerspec.Text:=IntToStr(Panel.View3DOptions.Perspective);
end;

procedure TFormTee3D.SetView3D(const AView:TView3DOptions);

  function FixAngle(const Angle:Single):Single;
  begin
    result:=Angle;
    while result>360 do result:=result-360;
    if result<0 then result:=360+result;
  end;

begin
  CBOrthogonal.IsChecked:=AView.Orthogonal;
  UDOrthoAngle.Value:=AView.OrthoAngle;
  UDTextSize.Value:=AView.FontZoom;
  CBZoomText.ItemIndex:=Ord(AView.ZoomText);
  SBZoom.Value:=AView.ZoomFloat;
  SBRotation.Value:=FixAngle(AView.RotationFloat);
  SBElevation.Value:=FixAngle(AView.ElevationFloat);
  SBHorizOffset.Value:=AView.HorizOffsetFloat;
  SBVertOffset.Value:=AView.VertOffsetFloat;
  SBPerspec.Value:=AView.Perspective;
end;

procedure TFormTee3D.RefreshPanel;
begin
  if Panel.Parent is TControl3D then
  begin
    SBElevation.Min:=0;
    IDepth:=TControl3D(Panel.Parent).Depth;
  end
  else
    IDepth:=0;

  CBView3d.IsChecked:=Panel.View3D;

  if Panel is TCustomAxisPanel then
  begin
    CBClipPoints.IsChecked:=TCustomAxisPanel(Panel).ClipPoints;
    Percent3D.Value:=TCustomAxisPanel(Panel).Chart3DPercent;
  end;

  SetView3D(Panel.View3DOptions);

  EnableControls;

  if Panel.Parent is TControl3D then
  begin
    CBOrthogonal.Visible:=False;
    LabelOrtho.Visible:=False;
    UDOrthoAngle.Visible:=False;
    SBPerspec.Visible:=False;
    LPerspec.Visible:=False;
    Label2.Visible:=False;
    L4.Visible:=False;
    LZoom.Visible:=False;
    SBZoom.Visible:=False;
  end;

  LBRender.ItemIndex:=0;
  LBRenderChange(Self);
end;

procedure TFormTee3D.EnableControls;
begin
  SBZoom.Enabled:=Panel.View3D;
  Percent3D.Enabled:=Panel.View3D;
  SBHorizOffset.Enabled:=Panel.View3D;
  SBVertOffset.Enabled:=Panel.View3D;
  CBOrthogonal.Enabled:=Panel.View3D;

  SBRotation.Enabled:=Panel.View3D and (not Panel.View3DOptions.Orthogonal);
  SBElevation.Enabled:=SBRotation.Enabled;
  SBPerspec.Enabled:=SBRotation.Enabled;

  UDOrthoAngle.Enabled:=Panel.View3DOptions.Orthogonal;
end;

class function TFormTee3D.CreateEditor(const AOwner: TComponent; const APanel: TCustomTeePanel):TForm;
begin
  result:=TFormTee3D.Create(AOwner);

  with TFormTee3D(result) do
  begin
    Panel:=APanel;

    AddRenders;
    RefreshPanel;
  end;
end;

procedure TFormTee3D.CreateViewsGallery;

  Function AddNewChart(const ATitle:String; Is3D:Boolean=False):TCustomChart;
  var t : Integer;
  begin
    result:=Views.CreateChart(TeeSeriesTypes[0]) as TCustomChart;

    with result do
    begin
      Walls.Back.Transparent:=False;
      Walls.Size:=5;
      SeriesList.ClearValues;
      Title.Text[0]:=ATitle;

      if Is3D then
      begin
        View3D:=True;
        View3DOptions.Orthogonal:=False;

        for t:=0 to SeriesCount-1 do
            Series[t].VertAxis:=aBothVertAxis;

        View3DOptions.Elevation:=360;
        Axes.Left.Labels:=True;
      end;
    end;
  end;

var t : Integer;
begin
  CreatingViews:=True;

  with Views do
  begin
    Smooth:=False;
    DisplaySub:=False;
    FocusRotation:=False;

    NumCols:=3;
    NumRows:=3;

    AddNewChart(TeeMsg_Default);

    with AddNewChart(TeeMsg_View2D) do
    begin
      View3D:=False;
      Axes.Left.Labels:=True;
    end;

    with AddNewChart(TeeMsg_ViewOrthoRight) do
    begin
      View3DOptions.Orthogonal:=True;
      Axes.Left.Labels:=True;
    end;

    with AddNewChart(TeeMsg_ViewOrthoLeft) do
    begin
      View3DOptions.Orthogonal:=True;
      Walls.Left.Hide;
      Walls.Right.Visible:=True;

      for t:=0 to SeriesCount-1 do
          Series[t].VertAxis:=aRightAxis;

      Axes.Right.ZPosition:=0;
      View3DOptions.OrthoAngle:=135;
    end;

    with AddNewChart(TeeMsg_View3DFront,True) do
    begin
      Walls.Left.Visible:=True;
      Walls.Right.Visible:=True;
      Axes.Right.ZPosition:=0;
      View3DOptions.Rotation:=360;
    end;

    with AddNewChart(TeeMsg_View3DRight,True) do
    begin
      Walls.Left.Visible:=True;
      Walls.Right.Hide;
      Axes.Right.ZPosition:=100;
      View3DOptions.Rotation:=300;
    end;

    with AddNewChart(TeeMsg_View3DLeft,True) do
    begin
      Walls.Left.Hide;
      Walls.Right.Visible:=True;
      Axes.Right.ZPosition:=0;
      Axes.Left.ZPosition:=100;
      View3DOptions.Rotation:=45;
    end;

    with AddNewChart(TeeMsg_Top,True) do
    begin
      View3DOptions.Rotation:=0;
      View3DOptions.Elevation:=270;
    end;

    with AddNewChart(TeeMsg_Back,True) do
    begin
      View3DOptions.Rotation:=180;
      View3DOptions.Elevation:=360;
    end;

    ResizeCharts;
  end;

  CreatingViews:=False;
end;

procedure TFormTee3D.CBClipPointsChange(Sender: TObject);
begin
  TCustomAxisPanel(Panel).ClipPoints:=CBClipPoints.IsChecked;
end;

procedure TFormTee3D.AddRenders;
var item : TListBoxItem;
begin
  if Panel.Canvas.ReferenceCanvas<>nil then
  begin
    item:=TListBoxItem.Create(Self);
    item.Text:=Panel.Canvas.ReferenceCanvas.ClassName;
    item.Text:=Copy(item.Text,2,Length(item.Text));

    LBRender.AddObject(item);
  end;
end;

function ScreenDensity:String;
{$IFDEF ANDROID}
{$IFNDEF D21}
var tmp: JDisplayMetrics;
{$ENDIF}
{$ENDIF}
begin
  result:='';

  {$IFDEF ANDROID}

  {$IFDEF D23}
  result:=IntToStr(TAndroidHelper.DisplayMetrics.densityDpi)+' DPI';
  {$ELSE}
  {$IFDEF D21}
  result:=IntToStr(GetJDisplayMetrics.densityDpi)+' DPI';
  {$ELSE}
  tmp:=GetJDisplayMetrics;

  if Assigned(tmp) then
     result:=IntToStr(tmp.densityDpi)+' DPI';
  {$ENDIF}
  {$ENDIF}
  {$ENDIF}
end;

procedure TFormTee3D.CBZoomTextChange(Sender: TObject);
begin
  Panel.View3DOptions.ZoomText:=TCanvasZoomText(CBZoomText.ItemIndex);
  UDTextSize.Enabled:=Panel.View3DOptions.ZoomText=ztManual;

  if Panel.Parent is TControl3D then
     Panel.Invalidate;
end;

procedure TFormTee3D.LBRenderChange(Sender: TObject);
var tmp : TCanvas;
begin
  CBBuffered.IsChecked:=Panel.Canvas.UseBuffer;

  tmp:=Panel.Canvas.ReferenceCanvas;

  if Assigned(tmp) then
  begin
    CBGPUSurface.IsChecked:=TCanvasStyle.NeedGPUSurface in tmp.GetCanvasStyle;
    CBClipRect.IsChecked:=TCanvasStyle.SupportClipRects in tmp.GetCanvasStyle;

    case tmp.Quality of
      TCanvasQuality.{$IFDEF D20}SystemDefault{$ELSE}ccSystemDefault{$ENDIF}: CBQuality.ItemIndex:=0;
      TCanvasQuality.{$IFDEF D20}HighPerformance{$ELSE}ccHighPerformance{$ENDIF}: CBQuality.ItemIndex:=1;
    else
      CBQuality.ItemIndex:=2;
    end;

    EScale.Text:=FormatFloat('0.##',tmp.Scale);
    CBBlending.IsChecked:=tmp.Blending;

    LDensity.Text:=ScreenDensity;
    LDensity.Visible:=LDensity.Text<>'';

  end;

  CBBlending.Enabled:=Assigned(tmp);
end;

procedure TFormTee3D.CBBlendingChange(Sender: TObject);
begin
  Panel.Canvas.ReferenceCanvas.Blending:=CBBlending.IsChecked;
end;

procedure TFormTee3D.CBBufferedChange(Sender: TObject);
begin
  Panel.Canvas.UseBuffer:=CBBuffered.IsChecked;
end;

end.
