{**********************************************}
{   TEqualizerSeries                           }
{   Equalizer Series                           }
{                                              }
{   Copyright (c) 1999-2020 by Steema Software }
{**********************************************}
unit TeeEqualizerSeries;

interface

uses {$IFNDEF LINUX}
     {$IFNDEF FMX}
     Windows,
     {$ENDIF}
     {$ENDIF}
     SysUtils, Classes,
     {$IFDEF FMX}
     FMX.Types, System.UITypes,
     {$ELSE}
     Graphics,extctrls,
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Engine, FMXTee.Chart, FMXTee.Canvas, FMXTee.Series, FMXTee.Series.Donut, FMXTee.Tools
     {$ELSE}
     TeEngine, Chart, TeCanvas, Series, TeeDonut, TeeTools
     {$ENDIF};

type
  TEqualizerSeries=class(TBarSeries)
  private
    FHiMarkers : TPointSeries;
    FDegradeInterval : Integer; //= 500D;
    FDownStep : Double; // = 0F;
    applyDegradation : Boolean; //= false;
    lastPointCount : Integer;
    hasPainted : Boolean;

    ITimer      : TTimer;

    procedure SetHighMarkers;
    procedure SetDegradeHigh(Value : Boolean);
    function GetDegradeHigh : Boolean;
    procedure TimerEvent(Sender:TObject);
  protected
    class Function GetEditorClass:String; override;
    Procedure PrepareForGallery; override;
    function GetHiMarkers : TPointSeries;
    procedure HideSeries(ASeries : TChartSeries);
    procedure DoBeforeDrawValues; override;
    procedure DoAfterDrawValues; override;
    procedure SetDegradeInterval(value : Integer);
    function GetDegradeInterval : Integer;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    Procedure DrawBar(BarIndex,StartPos,EndPos:Integer); override;
    Function DrawSeriesForward(ValueIndex:Integer):Boolean; override;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
  published
    property DegradeHigh:Boolean read GetDegradeHigh write SetDegradeHigh default false;
    property HighMarker:TPointSeries read GetHiMarkers;
    property DegradeInterval:Integer read GetDegradeInterval write SetDegradeInterval; // default 500;
    property DownStep:Double read FDownStep write FDownStep; // default 0;
  end;

implementation

uses
  {$IFDEF FMX}
  FMXTee.Constants, FMXTee.ProConstants, FMXTee.Procs
  {$ELSE}
  TeeConst, TeeProCo, TeeProcs
  {$ENDIF};


{ TEqualizerSeries }
class Function TEqualizerSeries.GetEditorClass:String;
begin
  result:='TEqualizerSeriesEditor'; // Do not localize
end;

constructor TEqualizerSeries.Create(AOwner: TComponent);
begin
  inherited;
  DegradeInterval := 500;
  DownStep := 0;
  lastPointCount := 0;

  GradientRelative:=True;
  self.Pen.Visible := False;

  Brush.Gradient.Visible := true;
  HighMarker.Color := Brush.Gradient.StartColor;

  HighMarker.Legend.Visible := false;
  HighMarker.ShowInEditor := false;
  HighMarker.Pointer.CenterPixel := False;

  HighMarker.Pointer.HorizSize :=  IBarSize div 2;
  HighMarker.Marks.Visible := false;
  HighMarker.Pointer.VertSize := 2;
  HighMarker.Pointer.Pen.Visible := False;

  Marks.Visible := false;
  self.RefreshSeries;

  ITimer:=TTimer.Create(Self);

  with ITimer do
  begin
    Interval:=FDegradeInterval;
    Enabled:=False;
    OnTimer:=TimerEvent;
  end;

  TimerEvent(Self);
end;

Destructor TEqualizerSeries.Destroy;
Begin
  ITimer.Free;
  if FHiMarkers <> nil then
  Begin
    FHiMarkers.Clear;
    FHiMarkers.Free;
  end;
  inherited;
end;

procedure TEqualizerSeries.DrawBar(BarIndex, StartPos, EndPos: Integer);
begin
  inherited;
end;

function TEqualizerSeries.DrawSeriesForward(ValueIndex: Integer): Boolean;
begin
    hasPainted := False;
    result:=true;
end;

Procedure TEqualizerSeries.SetParentChart(Const Value:TCustomAxisPanel);
begin
  inherited;
  if (ParentChart <> nil) then
  Begin
    ParentChart.ApplyZOrder := False;
    if (TChart(ParentChart).Legend.LegendStyle=lsAuto) then
      TChart(ParentChart).Legend.LegendStyle:=lsValues;
  end;
end;

type
  TSeriesAccess=class(TChartSeries);

function TEqualizerSeries.GetHiMarkers: TPointSeries;
begin
  if not assigned(FHiMarkers) Then
  Begin
    FHiMarkers := TPointSeries.Create(self);
    FHiMarkers.Title := 'HiMarker';
  end;

  TSeriesAccess(FHiMarkers).DontSerialize:=True;
  HideSeries(FHiMarkers);
  result:= FHiMarkers;
end;

procedure TEqualizerSeries.HideSeries(ASeries : TChartSeries);
begin
  ASeries.Legend.Visible := False;
  FHiMarkers.ShowInEditor := False;
end;

procedure TEqualizerSeries.DoBeforeDrawValues;
Var tmpRect : TRect;
Begin
  inherited;

  if Assigned(ParentChart) then
    ParentChart.View3D := False;

  tmpRect := CalcBarBounds(0);

  HighMarker.Pointer.HorizSize :=   Round((tmpRect.Right-tmpRect.Left) {$IFNDEF FMX}div{$ELSE}/{$ENDIF} 2);
  setHighMarkers();
end;

procedure TEqualizerSeries.DoAfterDrawValues;
Begin
  inherited;
  if hasPainted = False then  //provoke paint once of new markers
  Begin
    hasPainted := True;
    self.ParentChart.Draw;
  end;
end;

procedure TEqualizerSeries.SetDegradeInterval(value : Integer);
Begin
  if ITimer <> nil then
    ITimer.Interval := value;
  FDegradeInterval := Value;
end;

function TEqualizerSeries.GetDegradeInterval : Integer;
Begin
  result:= FDegradeInterval;
end;


procedure TEqualizerSeries.PrepareForGallery;
Var i : Integer;
begin
  inherited;

  FillSampleValues(5);
  HighMarker.FillSampleValues(5);
  //gallery only - accentuate pointer visibility
  HighMarker.Pointer.Pen.Color := {$IFDEF FMX}TAlphaColors.Lightgrey{$ELSE}clLtGray{$ENDIF};
  HighMarker.Pointer.Pen.Visible := True;
  for i:=0 to Count - 1 do
  Begin
    HighMarker.XValues[i] := self.XValues[i];
    HighMarker.YValues[i] := self.YValues[i];
  end;

  GetHorizAxis.Grid.Visible := False;
  RefreshSeries();
end;

procedure TEqualizerSeries.SetHighMarkers;
Var newFill : Boolean;
    i : Integer;
begin
      newFill := False;

      if ((ParentChart <> nil) and (HighMarker.ParentChart = nil)) then
      Begin
        HighMarker.ParentChart := self.ParentChart;
        HideSeries(HighMarker);
      end;

      if ((lastPointCount <> self.Count) or (XValues[0] <> HighMarker.XValues[0])) then
      begin
        newFill := true;
        FHiMarkers.Clear();
        FHiMarkers.FillSampleValues(self.Count);
      end;

      for i := 0 to self.Count - 1 do
      begin
        if (newFill) then
        begin
          FHiMarkers.XValues[i] := XValues[i];
          FHiMarkers.YValues[i] := YValues[i];
        end
        else
        begin
          if applyDegradation then
          begin
            if (FHiMarkers.YValues[i]-DownStep) >= YValues[i] then
              FHiMarkers.YValues[i] := FHiMarkers.YValues[i] - DownStep
            else
              FHiMarkers.YValues[i] := YValues[i];
          end;
          if (YValues[i] > FHiMarkers.YValues[i]) then
            FHiMarkers.YValues[i] := YValues[i];
        end;
      end;

      lastPointCount := HighMarker.Count;
end;

function TEqualizerSeries.GetDegradeHigh : Boolean;
Begin
  result := applyDegradation;
end;

procedure TEqualizerSeries.SetDegradeHigh(Value : Boolean);
Begin
  if (Value) then
  Begin
    applyDegradation := true;
    ITimer.Enabled:=True;
  end
  else
  Begin
    applyDegradation := false;
    ITimer.Enabled:=False;
        end;
  end;

procedure TEqualizerSeries.TimerEvent(Sender:TObject);
Begin
  if (ParentChart <> nil) then
    self.Repaint;
end;

initialization
  RegisterTeeSeries(TEqualizerSeries,@TeeMsg_EqualizerSeries,
                                  @TeeMsg_GalleryGauges,1);
finalization
  UnRegisterTeeSeries([TEqualizerSeries]);

end.
