{**********************************************}
{   TImagePointSeries and TDeltaPointSeries    }
{   Copyright (c) 1997-2020 by Steema Software }
{**********************************************}
unit ImaPoint;
{$I TeeDefs.inc}

interface

Uses 
     {$IFNDEF LINUX}
     {$IFNDEF FMX}
     Windows,
     {$ENDIF}
     {$ENDIF}

     SysUtils, Classes,

     {$IFDEF FMX}
     FMX.Types,
     FMXTee.Engine, FMXTee.Chart, FMXTee.Series, FMXTee.Canvas
     {$ELSE}
     Graphics, TeEngine, Chart, Series, TeCanvas
     {$ENDIF}
     ;

{  This unit contains two Series components:

   TImagePointSeries --  A point Series displaying images
   TDeltaPointSeries --  An ImagePoint Series displaying a different
                         image depending the previous point value.
}

type TCustomImagePointSeries=class;
     TGetImageEvent=Procedure( Sender:TCustomImagePointSeries;
                               ValueIndex:Integer;
                               Picture:TPicture) of object;

     TCustomImagePointSeries=class(TPointSeries)
     private
       FImageTransp : Boolean;
       FOnGetImage  : TGetImageEvent;

       IImage       : TTeePicture;

       function GetImagePoint:TTeePicture;
       procedure SetImagePoint(Const Value:TTeePicture);
       procedure SetImageTransp(Const Value:Boolean);
     protected
       procedure DrawValue(ValueIndex:Integer); override;
       Procedure PrepareForGallery; override;
     public
       Procedure DrawLegend(ValueIndex:Integer; Const Rect:TRect); override;

       property ImagePoint:TTeePicture read GetImagePoint write SetImagePoint;
       property ImageTransparent:Boolean read FImageTransp write SetImageTransp
                                         default False;
       property OnGetImage:TGetImageEvent read FOnGetImage
                                          write FOnGetImage;
     end;

     TImagePointSeries=class(TCustomImagePointSeries)
     private
       procedure SetupFields;
     public
       Constructor Create(AOwner:TComponent); override;
       Constructor CreatePicture(AOwner:TComponent; Picture:TTeePicture); virtual;
     published
       property ImagePoint;
       property ImageTransparent default True;
       property OnGetImage;
     end;

     TDeltaImageStyle=(disSmiles, disHands);

     TDeltaPointSeries=class(TCustomImagePointSeries)
     private
       FEqualImage   : TTeePicture;
       FGreaterImage : TTeePicture;
       FImageStyle   : TDeltaImageStyle;
       FLowerImage   : TTeePicture;

       procedure CalculateImage(ValueIndex:Integer);
       procedure LoadImages;
       procedure SetEqualImage(Const Value:TTeePicture);
       procedure SetGreaterImage(Const Value:TTeePicture);
       procedure SetImageStyle(Const Value:TDeltaImageStyle);
       procedure SetLowerImage(Const Value:TTeePicture);
     protected
       procedure DrawValue(ValueIndex:Integer); override;
     public
       Constructor Create(AOwner:TComponent); override;
       Destructor Destroy; override;

       Procedure DrawLegend(ValueIndex:Integer; Const Rect:TRect); override;
     published
       property EqualImage:TTeePicture read FEqualImage write SetEqualImage;
       property GreaterImage:TTeePicture read FGreaterImage write SetGreaterImage;
       property ImageStyle:TDeltaImageStyle read FImageStyle write
                                            SetImageStyle default disSmiles;
       property ImageTransparent;
       property LowerImage:TTeePicture read FLowerImage write SetLowerImage;

       { inherited from TCustomImagePointSeries }
       property OnGetImage;
     end;

implementation

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

{$R TeeImaPo.res}

{ TCustomImagePointSeries }

Procedure TCustomImagePointSeries.DrawLegend(ValueIndex:Integer; Const Rect:TRect);
begin
  ParentChart.Canvas.StretchDraw(Rect,IImage.Filtered);
end;

{ overrided DrawValue to draw an Image for each point }
procedure TCustomImagePointSeries.DrawValue(ValueIndex:Integer);
var R   : TRect;
    tmp : TGraphic;
begin
  if not IImage.Valid then
     inherited
  else
  With ParentChart,Canvas do
  begin
    { trigger the OnGetImage event if assigned... }
    if Assigned(FOnGetImage) then
       OnGetImage(Self,ValueIndex,IImage);

    { draw the image... }
    With R do
    begin
      Left:=CalcXPos(ValueIndex)-(Pointer.HorizSize div 2);
      Right:=Left+Pointer.HorizSize;
      Top:=CalcYPos(ValueIndex)-(Pointer.VertSize div 2);
      Bottom:=Top+Pointer.VertSize;
    end;

    tmp:=IImage.Filtered;

    {$IFNDEF FMX}
    tmp.Transparent:=FImageTransp;
    {$ENDIF}

    With ParentChart.Canvas do
    begin
      R:=CalcRect3D(R,StartZ);

      if ((R.Right-R.Left)=tmp.Width) and
         ((R.Bottom-R.Top)=tmp.Height) then
         Draw({$IFDEF FMX}Round{$ENDIF}(R.Left),{$IFDEF FMX}Round{$ENDIF}(R.Top),tmp)
      else
         StretchDraw(R,tmp);
    end;
  end;
end;

function TCustomImagePointSeries.GetImagePoint:TTeePicture;
begin
  result:=Pointer.Picture;
end;

procedure TCustomImagePointSeries.SetImagePoint(Const Value:TTeePicture);
begin
  Pointer.Picture:=Value;

  if Assigned(Value) then
  begin
    if Value.Width>0 then Pointer.HorizSize:=Value.Width;
    if Value.Height>0 then Pointer.VertSize:=Value.Height;
  end;
end;

procedure TCustomImagePointSeries.SetImageTransp(Const Value:Boolean);
begin
  SetBooleanProperty(FImageTransp,Value);
end;

Procedure TCustomImagePointSeries.PrepareForGallery;
begin
  inherited;
  ParentChart.View3DOptions.Orthogonal:=True;
end;

type
  TPointerAccess=class(TSeriesPointer);

{ TImagePointSeries }
Constructor TImagePointSeries.Create(AOwner:TComponent);
begin
  inherited;
  SetupFields;
end;

Constructor TImagePointSeries.CreatePicture(AOwner:TComponent; Picture:TTeePicture);
begin
  Create(AOwner);
  ImagePoint:=Picture;
end;

procedure TImagePointSeries.SetupFields;
begin
  IImage:=ImagePoint;

  TeeLoadBitmap(IImage.Bitmap,'TeeDefaultImage'); // Do not localize

  FImageTransp:=True;

  TPointerAccess(Pointer).ChangeHorizSize(IImage.Width);
  TPointerAccess(Pointer).ChangeVertSize(IImage.Height);
end;

{ TDeltaPointSeries }
Constructor TDeltaPointSeries.Create(AOwner:TComponent);
begin
  inherited;

  Pointer.Pen.Hide;

  FEqualImage:=TTeePicture.Create;
  FEqualImage.OnChange:=CanvasChanged;

  FGreaterImage:=TTeePicture.Create;
  FGreaterImage.OnChange:=CanvasChanged;

  FLowerImage:=TTeePicture.Create;
  FLowerImage.OnChange:=CanvasChanged;

  ImageStyle:=disSmiles;
  LoadImages;
end;

Destructor TDeltaPointSeries.Destroy;
begin
  FEqualImage.Free;
  FLowerImage.Free;
  FGreaterImage.Free;
  inherited;
end;

Procedure TDeltaPointSeries.DrawLegend(ValueIndex:Integer; Const Rect:TRect);
begin
  CalculateImage(ValueIndex);
  inherited;
end;

procedure TDeltaPointSeries.CalculateImage(ValueIndex:Integer);
var tmp         : TChartValue;
    tmpPrevious : TChartValue;
begin
  // "Equal" image used both for first point in the series, and for "-1" valueindex
  // (-1 is used when painting the legend symbol for the Series itself)
  IImage:=FEqualImage;

  if ValueIndex>0 then
  begin
    tmpPrevious:=MandatoryValueList.Value[ValueIndex-1];
    tmp:=MandatoryValueList.Value[ValueIndex];

    if tmp>tmpPrevious then IImage:=FGreaterImage
    else
    if tmp<tmpPrevious then IImage:=FLowerImage;
  end;
end;

{ overrided DrawValue to draw an Image for each point }
procedure TDeltaPointSeries.DrawValue(ValueIndex:Integer);
begin
  CalculateImage(ValueIndex);

  if IImage.Width>0 then
     TPointerAccess(Pointer).ChangeHorizSize(IImage.Width);

  if IImage.Height>0 then
     TPointerAccess(Pointer).ChangeVertSize(IImage.Height);

  inherited;

  IImage:=ImagePoint;
end;

procedure TDeltaPointSeries.SetEqualImage(Const Value:TTeePicture);
begin
  FEqualImage.Assign(Value);
end;

procedure TDeltaPointSeries.SetGreaterImage(Const Value:TTeePicture);
begin
  FGreaterImage.Assign(Value);
end;

procedure TDeltaPointSeries.SetLowerImage(Const Value:TTeePicture);
begin
  FLowerImage.Assign(Value);
end;

procedure TDeltaPointSeries.LoadImages;
begin
  if FImageStyle=disHands then
  begin
    TeeLoadBitmap(FEqualImage.Bitmap,'TeeEqualHand'); // Do not localize
    TeeLoadBitmap(FGreaterImage.Bitmap,'TeeGreaterHand'); // Do not localize
    TeeLoadBitmap(FLowerImage.Bitmap,'TeeLowerHand'); // Do not localize
  end
  else
  begin
    TeeLoadBitmap(FEqualImage.Bitmap,'TeeEqualSmile'); // Do not localize
    TeeLoadBitmap(FGreaterImage.Bitmap,'TeeGreaterSmile'); // Do not localize
    TeeLoadBitmap(FLowerImage.Bitmap,'TeeLowerSmile'); // Do not localize
  end;
end;

procedure TDeltaPointSeries.SetImageStyle(Const Value:TDeltaImageStyle);
begin
  if FImageStyle<>Value then
  begin
    FImageStyle:=Value;
    LoadImages;
    Repaint;
  end;
end;

initialization
  RegisterTeeSeries( TImagePointSeries,
           @TeeMsg_ImagePointSeries,
           @TeeMsg_GallerySamples, 1 );
  RegisterTeeSeries( TDeltaPointSeries,
           @TeeMsg_DeltaPointSeries,
           @TeeMsg_GallerySamples, 1 );
finalization
  UnRegisterTeeSeries([ TImagePointSeries,TDeltaPointSeries ]);
end.
