{**********************************************}
{   TBar3DSeries Component                     }
{   Copyright (c) 1996-2020 by Steema Software }
{**********************************************}
unit Bar3D;
{$I TeeDefs.inc}

{ This Series component is derived from TBarSeries.
  It adds a new property:  OffsetValues

  This new property allows to specify a different ORIGIN value
  for EACH bar point.

  This can be used with standard TBarSeries components to
  make a "Stacked-3D" chart type.
}
interface

uses {$IFNDEF LINUX}
     {$IFNDEF FMX}
     Windows, Messages,
     {$ENDIF}
     {$ENDIF}
     SysUtils, Classes,

     {$IFDEF FMX}
     FMX.Types,
     {$ELSE}
     Graphics, Controls, Forms, Dialogs,
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Canvas, FMXTee.Engine, FMXTee.Series
     {$ELSE}
     TeEngine, Series
     {$ENDIF}
     ;

type
  TBar3DSeries = class(TBarSeries)
  private
    { Private declarations }
    FOffsetValues : TChartValueList;
  protected
    { Protected declarations }
    Procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;
    Procedure SetOffsetValues(Value:TChartValueList);
    class Function SubGalleryStack:Boolean; override;
  public
    { Public declarations }
    Constructor Create(AOwner: TComponent); override;
    Function AddBar( Const AX,AY,AOffset:TChartValue;
                     Const AXLabel:String='';
                     AColor:TColor=clTeeColor):Integer;
    Function GetOriginValue(ValueIndex:Integer):Double; override;
    Function MaxYValue:TChartValue; override;
    Function MinYValue:TChartValue; override;
    Function PointOrigin(ValueIndex:Integer; SumAll:Boolean):Double; override;
  published
    { Published declarations }
    property OffsetValues:TChartValueList read FOffsetValues
                                          write SetOffsetValues;
  end;

implementation

Uses Math,
     {$IFDEF FMX}
     FMXTee.ProConstants, FMXTee.Chart
     {$ELSE}
     Chart, TeeProCo
     {$ENDIF}
     ;

{ TBar3DSeries }

Constructor TBar3DSeries.Create(AOwner: TComponent);
begin
  inherited;

  FOffsetValues:=TChartValueList.Create(Self,TeeMsg_ValuesOffset); { <-- "offset" storage }
end;

Procedure TBar3DSeries.SetOffsetValues(Value:TChartValueList);
begin
  SetChartValueList(FOffsetValues,Value); { standard method }
end;

// Calculate maximum Y value
Function TBar3DSeries.MaxYValue:TChartValue;
begin
  result:=inherited MaxYValue;

  if (MultiBar=mbNone) or (MultiBar=mbSide) then
     result:=Math.Max(result,FOffsetValues.MaxValue);
end;

// Calculate minimum Y value ( YValues and negative Offsets supported )
Function TBar3DSeries.MinYValue:TChartValue;
var t : Integer;
begin
  result:=inherited MinYValue;

  if (MultiBar=mbNone) or (MultiBar=mbSide) then
     for t:=0 to Count-1 do
         if FOffsetValues.Value[t]<0 then
            result:=Math.Min(result,YValues.Value[t]+FOffsetValues.Value[t]);
end;

Function TBar3DSeries.AddBar( Const AX,AY,AOffset:TChartValue;
                              Const AXLabel:String='';
                              AColor:TColor=clTeeColor):Integer;
begin
  FOffsetValues.TempValue:=AOffset;
  result:=AddXY(AX,AY,AXLabel,AColor);
end;

// Add some sample values to see something in design mode:
procedure TBar3DSeries.AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False);
var t : Integer;
    s : TSeriesRandomBounds;
begin
  s:=RandomBounds(NumValues);

  with s do
  for t:=1 to NumValues do
  Begin
    tmpY:=RandomValue(Round(DifY));

    AddBar( tmpX,
            10+Abs(tmpY),
            Abs(DifY/(1+RandomValue(5))));

    tmpX:=tmpX+StepX;
  end;
end;

// Overrides default bottom origin calculation
Function TBar3DSeries.PointOrigin(ValueIndex:Integer; SumAll:Boolean):Double;
begin
  result:=FOffsetValues.Value[ValueIndex];
end;

// Make this bar heigth to be: "offset" + "height"
Function TBar3DSeries.GetOriginValue(ValueIndex:Integer):Double;
begin
  result:=inherited GetOriginValue(ValueIndex)+FOffsetValues.Value[ValueIndex];
end;

class function TBar3DSeries.SubGalleryStack: Boolean;
begin
  result:=False; // Do not show stacked styles at sub-gallery
end;

// Register the Series at Chart gallery
initialization
  RegisterTeeSeries( TBar3DSeries, @TeeMsg_GalleryBar3D,
                                   @TeeMsg_GallerySamples, 1 );
finalization
  UnRegisterTeeSeries([TBar3DSeries]);
end.
