{*********************************************}
{  TeeChart Standard Series Types             }
{  Copyright (c) 1995-2020 by Steema Software }
{  All Rights Reserved                        }
{                                             }
{   TCustomSeries                             }
{     TLineSeries                             }
{      THorizLineSeries                       }
{     TAreaSeries                             }
{      THorizAreaSeries                       }
{     TPointSeries                            }
{   TCustomBarSeries                          }
{     TBarSeries                              }
{     THorizBarSeries                         }
{   TCircledSeries                            }
{     TPieSeries                              }
{   TFastLineSeries                           }
{                                             }
{*********************************************}
unit Series;
{$I TeeDefs.inc}

interface

{$IFDEF D10}
{$INLINE ON}  // Enable inlining for BDS 2006, RAD 2007, 2009 and 2010
{$IFNDEF D11}
{$ENDIF}
{$ELSE}
{$IFDEF D9}
{$INLINE OFF} // Disable inlining for Delphi 8 and Delphi 2005
{$ENDIF}
{$ENDIF}

{$IFDEF FPC}
{$DEFINE TEENORECTOFFSET}
{$ENDIF}

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

  {$IFDEF D18}
  System.Generics.Collections,
  {$ENDIF}
  
  {$IFDEF FMX}
  System.UITypes, System.Types, FMX.Types,

  {$IFDEF D17}
  System.UIConsts,
  {$ENDIF}

  {$IFDEF D19}
  FMX.Graphics,
  {$ENDIF}

  {$ELSE}

  {$IFDEF D16}
  System.UITypes, System.Types,
  {$IFDEF D17}
  System.UIConsts,
  {$ENDIF}
  {$ENDIF}

  Graphics, Controls, Forms,
  {$ENDIF}

  {$IFDEF D8}
  {$IFNDEF D16}
  Types,
  {$ENDIF}
  {$ENDIF}

  SysUtils, Classes,

  {$IFDEF FMX}
  FMXTee.Chart, FMXTee.Engine, FMXTee.Canvas, FMXTee.Procs

  {$IFNDEF TEELITEEMB}
  , FMXTee.Series.Spline
  {$ENDIF}

  {$ELSE}
  TeEngine, Chart, TeCanvas, TeeProcs, TeeSpline
  {$ENDIF}
  ;

const
  Tee_CircledShadowColor = TColor($A0A0A0);
  Tee_CircledEmbossColor = TColor($A0A0A0);

type
  // This record definition (TPieAngle) should be here at top of this "type" declaration,
  // to skip a C++ bug fixed in XE2:
  // http://qc.embarcadero.com/wc/qcmain.aspx?d=88686
  // "The symbol ... has zero size, or excessive offset in module ..."
  TPieAngle=Packed Record
    StartAngle : Double;
    MidAngle   : Double;
    EndAngle   : Double;
  end;

  TPieAngles=Array of TPieAngle;

  TCustomLineSeries=class(TChartSeries)
  protected
    Function GetLinePen:TChartPen;
  public
    property LinePen:TChartPen read GetLinePen write SetPen;
  end;

  TSeriesPointerItems=class(TSeriesPointer)
  private
    IList : TList{$IFDEF D18}<TSeriesPointer>{$ENDIF};

    procedure Delete(Index:Integer);
    procedure Ensure(Index:Integer);
    function Get(Index:Integer):TSeriesPointer;
    procedure InternalPut(Index:Integer; const Value:TSeriesPointer);
    procedure Put(Index:Integer; const Value:TSeriesPointer);
  protected
    function InternalGet(Index:Integer):TSeriesPointer;
  public
    Destructor Destroy; override;

    procedure Clear;
    function Count:Integer;
    property Items[Index:Integer]:TSeriesPointer read Get write Put; default;
  end;

  TCustomSeries=class;

  TSeriesClickPointerEvent=Procedure( Sender:TCustomSeries;
                                      ValueIndex:Integer;
                                      X, Y: Integer) of object;

  TCustomSeriesStack=(cssNone, cssOverlap, cssStack, cssStack100);

  TOnGetPointerStyle=Function( Sender:TChartSeries;
                               ValueIndex:Integer):TSeriesPointerStyle of object;

  TCustomSeriesDrawStyle=(dsSegments, dsAll, dsCurve);

  TCustomSeries=class(TCustomLineSeries)
  private
    FAreaBrush          : TTeeBrush;
    FAreaColor          : TColor;
    FAreaLinesPen       : TChartPen;
    FClickableLine      : Boolean;
    FClickTolerance     : Integer;
    FColorEachLine      : Boolean;
    FDark3D             : Boolean;
    FDrawArea           : Boolean;
    FDrawLine           : Boolean;
    FDrawStyle          : TCustomSeriesDrawStyle;
    FEmboss             : TTeeEmboss;
    FInflateMargins     : Boolean;
    FInvertedStairs     : Boolean;
    FLineHeight         : Integer;
    FOutLine            : TChartHiddenPen;
    FPointer            : TSeriesPointerItems;
    FPointerBehind      : Boolean;
    FShadow             : TTeeShadow;

    {$IFNDEF TEELITEEMB}
    FSmoothed           : Boolean;
    FSmoothFunction     : TSmoothingFunction;
    FSmoothSeries       : TCustomSeries;
    {$ENDIF}

    FStacked            : TCustomSeriesStack;
    FStairs             : Boolean;
    FDirty              : Boolean;

    { events }
    FOnClickPointer     : TSeriesClickPointerEvent;
    FOnGetPointerStyle  : TOnGetPointerStyle;

    {$IFDEF TEEOCX}
    FFastPoint : Boolean;
    {$ENDIF}

    { internal }
    BottomPos      : Integer;
    OldBottomPos   : Integer;
    OldX           : Integer;
    OldY           : Integer;
    OldColor       : TColor;

    {$IFNDEF TEELITEEMB}
    OldDataSource  : TComponent;
    {$ENDIF}

    tmpDark3DRatio : Double;
    HasTemplate    : Boolean;
    IClientRect    : TRect;


    function CalcPosLeftRight(Const ALimit:Double; ValueIndex,AnotherIndex:Integer):Integer;
    Function CalcStackedPos(ValueIndex:Integer; Value:Double):Integer;
    procedure ChangedBrush(Sender: TObject);
    function DoDrawAll(const APen:TTeePen; AColor:TColor; DrawPoints:Boolean):Boolean;
    Procedure DrawVisual(const R:TRect; const AItem:TSeriesPointer;
                         const AVisual:TVisualBlock; const AColor:TColor);
    Function GetAreaBrush:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
    Function GetLineBrush:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
    function GetOutLine:TChartHiddenPen;

    {$IFNDEF TEELITEEMB}
    Procedure FreeSmoothSeries;
    {$ENDIF}

    function HoverOverPointer:Integer;
    Procedure InternalCalcMargin(SameSide,Horizontal:Boolean; var A,B:Integer);
    Function PointOrigin(ValueIndex:Integer; SumAll:Boolean):Double;

    Procedure SetAreaBrush(const Value:TChartBrush);
    Procedure SetAreaBrushStyle(const Value:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
    Procedure SetAreaColor(Value:TColor);
    Procedure SetAreaLinesPen(const Value:TChartPen);
    procedure SetColorEachLine(const Value: Boolean);
    Procedure SetDark3D(Value:Boolean);
    Procedure SetDrawArea(Value:Boolean);
    procedure SetDrawStyle(const Value:TCustomSeriesDrawStyle);
    Procedure SetEmboss(const Value:TTeeEmboss);
    Procedure SetGradient(const Value:TChartGradient);
    Procedure SetInflateMargins(Value:Boolean);
    Procedure SetInvertedStairs(Value:Boolean);
    Procedure SetLineBrush(const Value:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
    Procedure SetLineHeight(Value:Integer);
    procedure SetOutLine(const Value: TChartHiddenPen);
    Procedure SetPointer(const Value:TSeriesPointerItems);
    Procedure SetPointerBehind(const Value:Boolean);
    Procedure SetShadow(const Value:TTeeShadow);
    Procedure SetStacked(const Value:TCustomSeriesStack);
    Procedure SetStairs(Value:Boolean);
    {$IFDEF TEEOCX}
    Procedure SetFastPoint(Value:Boolean);
    {$ENDIF}
  protected
    DisplayLast : Single; // from 0..1, default 1 = all

    Procedure AssignFormatting(Source:TChartSeries); override;
    Procedure CalcHorizMargins(var LeftMargin,RightMargin:Integer); override;
    Function CalcMarkLength:Integer; virtual;
    function CalcMinMaxValue(IsMin:Boolean):TChartValue; virtual;
    Procedure CalcVerticalMargins(var TopMargin,BottomMargin:Integer); override;
    Procedure CalcZOrder; override;

    Function ClickedPointer( ValueIndex,tmpX,tmpY:Integer;
                             x,y:Integer):Boolean; override;

    Procedure DoBeforeDrawChart; override;
    function DoGetPointerStyle(ValueIndex:Integer):TSeriesPointerStyle;

    Procedure DrawAllValues; override;
    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
    Procedure DrawPointer(AX,AY:Integer; AColor:TColor; ValueIndex:Integer); dynamic;
    procedure DrawValue(ValueIndex:Integer); override;
    procedure DrawHiddenValue(ValueIndex:Integer; const StartValue,EndValue:TAxisValue;
                              GapSize:Integer; Axis:TChartAxis); override;

    Function GetAreaBrushColor(AColor:TColor):TColor;
    class Function GetEditorClass:String; override;
    Function GetGradient:TChartGradient; virtual;
    procedure InitDrawValue(ValueIndex:Integer);

    property Gradient:TChartGradient read GetGradient write SetGradient;

    procedure LinePrepareCanvas(const ACanvas:TCanvas3D; tmpColor:TColor);
    procedure MouseDown(ValueIndex:Integer; AtPointer:Boolean; Button:TMouseButton;
                        Shift: TShiftState; X, Y: Integer); override;
    procedure PreparePointer(ValueIndex:Integer); virtual;
    function PrepareHoverPen(ValueIndex:Integer):Boolean; override;
    function PreparePointerHoverPen(ValueIndex:Integer):Boolean;
    function HoverPointerEnabled:Boolean;
    function HoverEnabled:Boolean; override;

    function SameClassOrigin(ASeries:TChartSeries):Boolean; virtual;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;

    {$IFNDEF TEELITEEMB}
    Procedure SetSmoothed(const Value:Boolean); virtual;
    Procedure SmoothSeries;
    {$ENDIF}

    function ShouldHoverMark(ValueIndex:Integer):Boolean; override;
    property Stacked:TCustomSeriesStack read FStacked write SetStacked default cssNone;
    property Transparency;
    Function IsPointInChartRect(ValueIndex:Integer):Boolean; override;
    function RaiseClicked:Boolean; override;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
    Procedure CalcFirstLastVisibleIndex; override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Function CalcYPos(ValueIndex:Integer):Integer; override;
    Procedure Clear; override;
    Function Clicked(x,y:Integer):Integer; override;
    Procedure Delete(ValueIndex:Integer); override;
    Function GetOriginPos(ValueIndex:Integer):Integer; virtual;
    Function MaxXValue:TChartValue; override;
    Function MinXValue:TChartValue; override;
    Function MaxYValue:TChartValue; override;
    Function MinYValue:TChartValue; override;
    procedure ResetBrush; virtual;
    procedure SwapValueIndex(a,b:Integer); override;

    property AreaBrush:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}
                          read GetAreaBrush write SetAreaBrushStyle
                          default bsSolid;
    property AreaChartBrush:TChartBrush read FAreaBrush write SetAreaBrush;
    property AreaColor:TColor read FAreaColor write SetAreaColor default clTeeColor;
    property AreaLinesPen:TChartPen read FAreaLinesPen write SetAreaLinesPen;
    property ClickableLine:Boolean read FClickableLine write FClickableLine default True;
    property ClickTolerance:Integer read FClickTolerance write FClickTolerance default 0;
    property ColorEachLine:Boolean read FColorEachLine write SetColorEachLine default True;
    property Dark3D:Boolean read FDark3D write SetDark3D default True;
    property DrawArea:Boolean read FDrawArea write SetDrawArea default False;
    property DrawStyle:TCustomSeriesDrawStyle read FDrawStyle write SetDrawStyle default dsSegments;
    property Emboss:TTeeEmboss read FEmboss write SetEmboss;
    property InflateMargins:Boolean read FInflateMargins write SetInflateMargins default True;
    property InvertedStairs:Boolean read FInvertedStairs write SetInvertedStairs default False;
    property LineBrush:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}
                         read GetLineBrush write SetLineBrush default bsSolid;
    property LineHeight:Integer read FLineHeight write SetLineHeight default 0;
    property OutLine:TChartHiddenPen read GetOutLine write SetOutLine;
    property Pointer:TSeriesPointerItems read FPointer write SetPointer;
    property PointerBehind:Boolean read FPointerBehind write SetPointerBehind default False;

    {$IFNDEF TEELITEEMB}
    property Smoothed:Boolean read FSmoothed write SetSmoothed default False;
    {$ENDIF}

    property Shadow:TTeeShadow read FShadow write SetShadow;
    property Stairs:Boolean read FStairs write SetStairs default False;
    {$IFDEF TEEOCX}
    property FastPoint:Boolean read FFastPoint write SetFastPoint default False;
    {$ENDIF}

    { events }
    property OnClickPointer:TSeriesClickPointerEvent read FOnClickPointer
                                                     write FOnClickPointer;
  published
    { events }
    property OnGetPointerStyle:TOnGetPointerStyle read FOnGetPointerStyle
                                                  write FOnGetPointerStyle;
  end;

  TLineSeries=Class(TCustomSeries)
  protected
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    Procedure PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                   var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Procedure Assign(Source:TPersistent); override;
  published
    property Active;
    property ColorEachLine;
    property ColorEachPoint;
    property ColorSource;
    property Cursor;
    property Depth;
    property Emboss;
    property Gradient;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;  { after parentchart }
    property PercentFormat;
    property SeriesColor;
    property Shadow;
    property ShowInLegend;
    {$IFNDEF TEELITEEMB}
    //property Smoothed;
    {$ENDIF}
    property Stacked;
    property Title;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    property Brush;
    property ClickableLine;
    property ClickTolerance;
    property Dark3D;
    property DrawStyle;
    property InflateMargins;
    property InvertedStairs;
    property LineBrush;
    property LineHeight;
    property LinePen;
    property OutLine;
    property Pointer;
    property PointerBehind;
    property Stairs;
    property TreatNulls;
    property XValues;
    property YValues;
    property Transparency;

    { event }
    property OnClickPointer;
  end;

  THorizLineSeries=class(TLineSeries)
  protected
    Function CalcMarkLength:Integer; override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
  public
    Constructor Create(AOwner: TComponent); override;
  end;

  TPointSeries=Class(TCustomSeries)
  private
    Procedure SetFixed;
  protected
    class Function CanDoExtra:Boolean; virtual;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    class Function GetEditorClass:String; override;
    Procedure PrepareForGallery; override;
    Procedure SetColorEachPoint(Value:Boolean); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
    procedure SetTransparency(const Value: TTeeTransparency); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Procedure Assign(Source:TPersistent); override;
  published
    property Active;
    property ColorEachPoint;
    property ColorSource;
    property Cursor;
    property Depth;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;  { after parentchart }
    property PercentFormat;
    property SeriesColor;
    property ShowInLegend;
    property Stacked;
    property Title;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    // Published inherited properties
    property ClickableLine;
    property ClickTolerance;
    property Pointer;
    property Transparency;
    property XValues;
    property YValues;

    { events }
    property OnClickPointer;
  end;

  TMultiArea=(maNone,maStacked,maStacked100);

  TAreaSeries=Class(TCustomSeries)
  private
    FOrigin     : Double;
    FStackGroup : Integer;
    FUseOrigin  : Boolean;

    Function GetMultiArea:TMultiArea;
    Procedure SetMultiArea(Value:TMultiArea);
    Procedure SetOrigin(Const Value:Double);
    Procedure SetStackGroup(const Value:Integer);
    Procedure SetUseOrigin(Value:Boolean);
  protected
    Procedure CalcZOrder; override;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    class Function GetEditorClass:String; override;
    Procedure PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                   var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}); override;
    function SameClassOrigin(ASeries:TChartSeries):Boolean; override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
  public
    Constructor Create(AOwner: TComponent); override;

    Procedure Assign(Source:TPersistent); override;
    Function DrawSeriesForward(ValueIndex:Integer):Boolean; override;
    Function GetOriginPos(ValueIndex:Integer):Integer; override;
    Function MaxXValue:TChartValue; override;
    Function MinXValue:TChartValue; override;
    Function MaxYValue:TChartValue; override;
    Function MinYValue:TChartValue; override;
    procedure ResetBrush; override;
  published
    property Active;
    property ColorEachLine;
    property ColorEachPoint;
    property ColorSource;
    property Cursor;
    property Depth;
    property Gradient;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;  // After ParentChart
    property PercentFormat;
    property SeriesColor;
    property ShowInLegend;
    {$IFNDEF TEELITEEMB}
    //property Smoothed;
    {$ENDIF}
    property Title;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    property AreaBrush;
    property AreaChartBrush;
    property AreaColor;
    property AreaLinesPen;
    property ClickableLine;
    property ClickTolerance;
    property Dark3D;
    property DrawArea;
    property DrawStyle;
    property InflateMargins;
    property InvertedStairs;
    property LinePen;
    property MultiArea:TMultiArea read GetMultiArea write SetMultiArea default maNone;
    property Pointer;
    property PointerBehind;
    property Stairs;
    property StackGroup:Integer read FStackGroup write SetStackGroup default 0;
    property Transparency;
    property TreatNulls;
    property UseYOrigin:Boolean read FUseOrigin write SetUseOrigin default False;
    property XValues;
    property YOrigin:Double read FOrigin write SetOrigin;
    property YValues;

    { events }
    property OnClickPointer;
  end;

  THorizAreaSeries=class(TAreaSeries)
  protected
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Function NumSampleValues:Integer; override;
  end;

  TSelfStack=class(TPersistent)
  private
    FUsePosition : Boolean;
    FPosition : TChartValue;

    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    IParent : TChartSeries;

    procedure SetPosition(const Value:TChartValue);
    Procedure SetUsePosition(const Value:Boolean);
  public
    procedure Assign(Source:TPersistent); override;
  published
    property Position:TChartValue read FPosition write SetPosition;
    property UsePosition:Boolean read FUsePosition write SetUsePosition default False;
  end;

  TMultiBar=(mbNone,mbSide,mbStacked,mbStacked100,mbSideAll,mbSelfStack);

  TCustomBarSeries=class;

  TBarStyle = (bsRectangle, bsPyramid, bsInvPyramid,
              bsCilinder, bsEllipse, bsArrow, bsRectGradient, bsCone, bsBevel,
              bsSlantCube, bsDiamond, bsInvArrow, bsInvCone, bsCustom, bsRoundRectangle);

  TBarRound = (brNone, brAtValue, brBothEnds);

  TGetBarBoundsEvent=procedure( Sender:TCustomBarSeries; ValueIndex:Integer;
                                var Rect:TRect) of object;

  TGetBarStyleEvent=procedure( Sender:TCustomBarSeries; ValueIndex:Integer;
                               var TheBarStyle:TBarStyle) of object;

  TMarksLocation=(mlStart, mlCenter, mlEnd);

  TCustomBarSeries=class(TChartSeries)
  private
    FAutoBarSize     : Boolean;
    FBarRound        : TBarRound;
    FBarStyle        : TBarStyle;
    FBarWidthPercent : Integer;
    FBevelSize       : Integer;
    FConePercent     : Integer;
    FDark3D          : Boolean;
    FDarkPen         : Integer;
    FDepthPercent    : Integer;
    FEmboss          : TTeeEmboss;
    FGradientRelative: Boolean;
    FMarksLocation   : TMarksLocation;
    FMarksOnBar      : Boolean;
    FMultiBar        : TMultiBar;
    FOffsetPercent   : Integer;
    FOrigin          : Double;
    FRoundSize       : Integer;
    FSelfStack       : TSelfStack;
    FShadow          : TTeeShadow;
    FSideMargins     : Boolean;
    FSides           : Integer;
    FStackGroup      : Integer;
    FTickLines       : TChartHiddenPen;
    FUseOrigin       : Boolean;

    { events }
    FOnGetBarStyle   : TGetBarStyleEvent;

    { internal }
    FBarBounds     : TRect;
    INumBars       : Integer;
    IMaxBarPoints  : Integer;
    IOrderPos      : Integer;
    IPreviousCount : Integer;

    Function CreateBlend(Transp:Integer):TTeeBlend;
    Procedure DrawBevel;
    function GetAutoMarkPosition:Boolean;
    Function GetBarBrush:TChartBrush;
    Function GetBarPen:TChartPen;
    Function GetBarStyle(ValueIndex:Integer):TBarStyle;
    function GetGradient:TTeeGradient;
    function GetTickLines:TChartHiddenPen;

    function PointInCube(const P:TPoint; const R:TRect; Z1,Z2:Integer):Boolean;
    Function PointInDiamond(const P:TPoint; X1,Y1,X2,Y2:Integer):Boolean;
    Procedure PrepareBarPen(ValueIndex:Integer);
    Procedure SetAutoBarSize(Value:Boolean);
    Procedure SetAutoMarkPosition(Value:Boolean);
    Procedure SetBarWidthPercent(Value:Integer);
    Procedure SetOffsetPercent(Value:Integer);
    Procedure SetBarRound(Value:TBarRound);
    Procedure SetBarStyle(Value:TBarStyle);
    procedure SetBevelSize(const Value: Integer);
    procedure SetConePercent(const Value: Integer);
    Procedure SetDark3D(Value:Boolean);
    Procedure SetDarkPen(Value:Integer);
    Procedure SetDepthPercent(Value:Integer);
    procedure SetEmboss(Const Value:TTeeEmboss);
    Procedure SetGradient(const Value:TTeeGradient);
    Procedure SetGradientRelative(const Value:Boolean);
    Procedure SetMarksOnBar(Value:Boolean);
    procedure SetMarksLocation(const Value:TMarksLocation);
    Procedure SetMultiBar(Value:TMultiBar);
    Procedure SetOrigin(Const Value:Double);
    Procedure SetOtherBars(SetOthers:Boolean);
    procedure SetRoundSize(Value:Integer);
    procedure SetShadow(Const Value:TTeeShadow);
    procedure SetSelfStack(const Value:TSelfStack);
    Procedure SetSideMargins(Value:Boolean);
    procedure SetSides(const Value: Integer);
    Procedure SetStackGroup(Value:Integer);
    procedure SetTickLines(const Value: TChartHiddenPen);
    Procedure SetUseOrigin(Value:Boolean);

    // internal
    function CalcBarGradient(ValueIndex:Integer):Boolean;
    Procedure CalcBarWidth;
    Procedure InternalApplyBarMargin(var MarginA,MarginB:Integer);
    Function InternalGetOriginPos(ValueIndex:Integer; DefaultOrigin:Integer):Integer;
    Function MaxMandatoryValue(Const Value:Double):Double;
    Function MinMandatoryValue(Const Value:Double):Double;
    function NotMandatoryAxis:TChartAxis;
    procedure SwapGradientColors;
  protected
    IBarSize         : Integer;
    FCustomBarSize   : Integer;
    FOnCalcBounds    : TGetBarBoundsEvent;

    Procedure AddSampleValues(NumValues:Integer; Sequential:Boolean=False); override;
    Procedure AssignFormatting(Series:TChartSeries); override;
    procedure CalcDepthPositions; override;
    Procedure CalcZOrder; override;
    class procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    Procedure DoBeforeDrawChart; override;
    Procedure DoBeforeDrawValues; override;
    procedure DrawCylinderTicks(TickPos:Integer);
    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
    function DrawSeriesAscending:Boolean; override;
    Procedure DrawTickLine(TickPos:Integer; AStyle:TBarStyle); dynamic;
    Procedure DrawTickLines(StartPos,EndPos:Integer; AStyle:TBarStyle);
    procedure DrawValue(ValueIndex:Integer); override;
    class Function GetEditorClass:String; override;
    Function InternalCalcMarkLength(ValueIndex:Integer):Integer; virtual;
    Function InternalClicked(ValueIndex:Integer; P:TPoint):Boolean; virtual;
    Function MoreSameZOrder:Boolean; override;
    Procedure PrepareForGallery; override;
    Procedure PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                   var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}); override;
    Procedure SetCustomBarSize(Value:Integer);
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    procedure SetPen(const Value: TChartPen); override;
    Function SortDrawPoints(FirstPoint:Integer;
                            LastPoint:Integer; SingleSeries:Boolean):TIntArray; override;
    Function SortDrawSeries(FirstSeries: Integer; LastSeries: Integer;
                            ValueIndex: Integer):TIntArray; override;
    Function IsPointInChartRect(ValueIndex:Integer):Boolean; override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
    class Function SubGalleryStack:Boolean; virtual;
  public
    NormalBarColor : TColor;

    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    Function AddBar(Const AValue:Double; Const ALabel:String; AColor:TColor):Integer;
    Function ApplyBarOffset(Position:Integer):Integer;
    Procedure Assign(Source:TPersistent); override;
    Function BarMargin:Integer;
    Procedure BarRectangle(BarColor:TColor; ALeft,ATop,ARight,ABottom:Integer; Rounded:Boolean=False); overload;
    Procedure BarRectangle(BarColor:TColor; const R:TRect; Rounded:Boolean=False); overload;
    Function CalcBarBounds(ValueIndex:Integer):TRect; virtual; abstract;
    Function CalcMarkLength(ValueIndex:Integer):Integer;
    Function Clicked(x,y:Integer):Integer; override;
    Procedure DrawBar(BarIndex,StartPos,EndPos:Integer); virtual; abstract;
    function DrawValuesForward:Boolean; override;
    Function NumSampleValues:Integer; override;
    Function PointOrigin(ValueIndex:Integer; SumAll:Boolean):Double; virtual;
    Procedure SetPenBrushBar(BarColor:TColor; ValueIndex:Integer=-1);
    Procedure CalcFirstLastVisibleIndex; override;

    property BarBounds:TRect read FBarBounds;
  published
    property Active;
    property BarBrush:TChartBrush read GetBarBrush write SetBrush;
    property BarPen:TChartPen read GetBarPen write SetPen;
    property BevelSize:Integer read FBevelSize write SetBevelSize default 1;
    property ColorEachPoint;
    property ColorSource;
    property ConePercent:Integer read FConePercent write SetConePercent
                                default 0;
    property Cursor;
    property DarkPen:Integer read FDarkPen write SetDarkPen default 0;
    property Depth;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;
    property PercentFormat;
    property RoundSize:Integer read FRoundSize write SetRoundSize default 16;
    property SeriesColor;
    property ShowInLegend;
    property Title;
    property Transparency;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    property AutoBarSize:Boolean read FAutoBarSize write SetAutoBarSize default False;
    property AutoMarkPosition:Boolean read GetAutoMarkPosition write SetAutoMarkPosition stored False; // Deprecated. See Marks.Automatic.Move
    property BarRound:TBarRound read FBarRound write SetBarRound
                                default brAtValue;
    property BarStyle:TBarStyle read FBarStyle write SetBarStyle
                                default bsRectangle;
    property BarWidthPercent:Integer read FBarWidthPercent
                                     write SetBarWidthPercent default 70;
    property Dark3D:Boolean read FDark3D write SetDark3D default True;
    property DepthPercent:Integer read FDepthPercent write SetDepthPercent default 100;
    property Emboss:TTeeEmboss read FEmboss write SetEmboss;
    property Gradient:TTeeGradient read GetGradient write SetGradient;
    property GradientRelative:Boolean read FGradientRelative write SetGradientRelative default False;
    property MarksLocation:TMarksLocation read FMarksLocation write SetMarksLocation
                                          default mlEnd;
    property MarksOnBar:Boolean read FMarksOnBar write SetMarksOnBar default False;
    property MultiBar:TMultiBar read FMultiBar write SetMultiBar default mbSide;

    property OffsetPercent:Integer read FOffsetPercent
                                   write SetOffsetPercent default 0;

    property SelfStack:TSelfStack read FSelfStack write SetSelfStack;
    property Shadow:TTeeShadow read FShadow write SetShadow;
    property SideMargins:Boolean read FSideMargins write SetSideMargins default True;
    property Sides:Integer read FSides write SetSides default 32;
    property StackGroup:Integer read FStackGroup write SetStackGroup default 0;
    property TickLines:TChartHiddenPen read GetTickLines write SetTickLines;
    property UseYOrigin:Boolean read FUseOrigin write SetUseOrigin default True;
    property YOrigin:Double read FOrigin write SetOrigin;

    { inherited published }
    property XValues;
    property YValues;

    { events }
    property OnGetBarStyle:TGetBarStyleEvent read FOnGetBarStyle write
                                             FOnGetBarStyle;
  end;

  TBarSeries=class(TCustomBarSeries)
  protected
    Procedure CalcHorizMargins(var LeftMargin,RightMargin:Integer); override;
    procedure CalcSelectionPos(ValueIndex:Integer; out X,Y:Integer); override;
    Procedure CalcVerticalMargins(var TopMargin,BottomMargin:Integer); override;
    Procedure DrawTickLine(TickPos:Integer; AStyle:TBarStyle); override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
    Function InternalCalcMarkLength(ValueIndex:Integer):Integer; override;
    Function InternalClicked(ValueIndex:Integer; P:TPoint):Boolean; override;
  public
    Constructor Create(AOwner:TComponent); override;

    Function CalcBarBounds(ValueIndex:Integer):TRect; override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Function CalcYPos(ValueIndex:Integer):Integer; override;
    Procedure DrawBar(BarIndex,StartPos,EndPos:Integer); override;
    Function DrawSeriesForward(ValueIndex:Integer):Boolean; override;
    Function GetOriginPos(ValueIndex:Integer):Integer;
    Function MaxXValue:TChartValue; override;
    Function MinXValue:TChartValue; override;
    Function MaxYValue:TChartValue; override;
    Function MinYValue:TChartValue; override;

    property BarWidth:Integer read IBarSize;
  published
    property CustomBarWidth:Integer read FCustomBarSize
                                    write SetCustomBarSize default 0;
  end;

  THorizBarSeries=class(TCustomBarSeries)
  protected
    Procedure CalcHorizMargins(var LeftMargin,RightMargin:Integer); override;
    procedure CalcSelectionPos(ValueIndex:Integer; out X,Y:Integer); override;
    Procedure CalcVerticalMargins(var TopMargin,BottomMargin:Integer); override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
    function DrawSeriesAscending:Boolean; override;
    Procedure DrawTickLine(TickPos:Integer; AStyle:TBarStyle); override;
    Function InternalCalcMarkLength(ValueIndex:Integer):Integer; override;
    Function InternalClicked(ValueIndex:Integer; P:TPoint):Boolean; override;
    function InvertedLegend:Boolean; override;
  public
    Constructor Create(AOwner:TComponent); override;

    Function CalcBarBounds(ValueIndex:Integer):TRect; override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Function CalcYPos(ValueIndex:Integer):Integer; override;
    Procedure DrawBar(BarIndex,StartPos,EndPos:Integer); override;
    Function DrawSeriesForward(ValueIndex:Integer):Boolean; override;
    function DrawValuesForward:Boolean; override;
    Function GetOriginPos(ValueIndex:Integer):Integer;
    Function MaxXValue:TChartValue; override;
    Function MinXValue:TChartValue; override;
    Function MaxYValue:TChartValue; override;
    Function MinYValue:TChartValue; override;

    property BarHeight:Integer read IBarSize;
  published
    property CustomBarHeight:Integer read FCustomBarSize
                                     write SetCustomBarSize default 0;
  end;

  TFrameBrush=class(TChartBrush)
  private
    FSize : Single;
    IDefault : Single;

    function IsSizeStored:Boolean;
    procedure InitSize(const ASize:Single);
    procedure SetSize(const Value:Single);
  published
    property Size:Single read FSize write SetSize stored IsSizeStored;
  end;

  TFramedBorder=class(TTeeCustomShapeBrushPen)
  private
    FCircled : Boolean;
    FEmboss  : TTeeEmboss;
    FInner   : TFrameBrush;
    FMiddle  : TFrameBrush;
    FOuter   : TFrameBrush;
    FShadow  : TTeeShadow;
    FWidth   : Integer;

    procedure GetPalette;
    function IsCircledStored:Boolean;
    function IsVisibleStored:Boolean;
    procedure ReadParentSeries(Reader:TReader); // obsolete
    procedure SetCircled(const Value: Boolean);
    procedure SetEmboss(const Value: TTeeEmboss);
    procedure SetInner(const Value: TFrameBrush);
    procedure SetMiddle(const Value: TFrameBrush);
    procedure SetOuter(const Value: TFrameBrush);
    procedure SetShadow(const Value: TTeeShadow);
    procedure SetWidth(const Value: Integer);
  protected
    INoBrush       : Boolean;
    DefaultCircled : Boolean;
    DefaultVisible : Boolean;

    procedure DefineProperties(Filer: TFiler); override;
  public
    Constructor Create; overload; virtual;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;
    function CalcWidth(const R:TRect):Single;
    procedure Draw(R:TRect);  virtual;
    procedure SetDefaultCircled(const Value:Boolean);
  published
    property Brush;
    property Circled:Boolean read FCircled write SetCircled
                             stored IsCircledStored;
    property Emboss:TTeeEmboss read FEmboss write SetEmboss;
    property InnerBrush:TFrameBrush read FInner write SetInner;
    property MiddleBrush:TFrameBrush read FMiddle write SetMiddle;
    property OuterBrush:TFrameBrush read FOuter write SetOuter;
    property Shadow:TTeeShadow read FShadow write SetShadow;
    property Visible stored IsVisibleStored;
    property Width:Integer read FWidth write SetWidth default 10;
  end;

  TCircularFrame=class(TFramedBorder)
  private
    FRotationAngle: Integer;
    FTotalAngle: Integer;

    //IPoints: TPointArray;

    procedure SetRotationAngle(const Value: Integer);
    procedure SetTotalAngle(const Value: Integer);
  public
    //BandRadius: Array[0..2] of Double;
    //ExtendInner: Boolean;

    FrameElementWidths: Array[0..2] of Double; // % Radius

    {
    InnerBandPoints: TFourPoints;
    MiddleBandPoints: TFourPoints;
    OuterBandPoints: TFourPoints;
    }

    Constructor Create; override;

    procedure Assign(Source:TPersistent); override;
    procedure Draw(R: TRect); override;
  published
    property RotationAngle:Integer read FRotationAngle write SetRotationAngle default 0;
    property TotalAngle:Integer read FTotalAngle write SetTotalAngle default 360;
  end;

  TCircledSeries=class(TChartSeries)
  private
    FCircleBrush        : TTeeBrush;
    FCircled            : Boolean;
    FCustomXRadius      : Integer;
    FCustomYRadius      : Integer;
    FEmboss             : TTeeEmboss;
    FFrame              : TFramedBorder;
    FRotationAngle      : Integer;
    FShadow             : TTeeShadow;
    FUniqueCustomRadius : Boolean;
    FXRadius            : Integer;
    FYRadius            : Integer;

    { internal }
    IBack3D         : TView3DOptions;
    FCircleWidth    : Integer;
    FCircleHeight   : Integer;
    FCircleXCenter  : Integer;
    FCircleYCenter  : Integer;
    FCircleRect     : TRect;

    function GetCircleBackColor:TColor;
    function GetFrame: TFramedBorder;
    function HasFrame:Boolean;
    Procedure SetBackupProperties;
    procedure SetCircleBackColor(Value:TColor);
    procedure SetCircleBrush(const Value:TTeeBrush);
    Procedure SetCircled(Value:Boolean);
    procedure SetCustomXRadius(Value:Integer);
    procedure SetCustomYRadius(Value:Integer);
    Procedure SetEmboss(const Value:TTeeEmboss);
    procedure SetFrame(const Value:TFramedBorder);
    procedure SetOtherCustomRadius(IsXRadius:Boolean; Value:Integer);
    procedure SetUniqueCustomRadius(Value:Boolean);
    Procedure SetShadow(const Value:TTeeShadow);
  protected
    IRotDegree            : Double;
    IAdjustCircleForMarks : Boolean;

    Procedure AdjustCircleRect;
    Procedure AssignFormatting(Source:TChartSeries); override;
    Function CalcCircleBackColor:TColor;
    Procedure CalcRadius; virtual;
    Procedure CheckOtherSeriesMarks; dynamic;
    Procedure DoBeforeDrawValues; override;
    Procedure DrawCircleShadow;
    procedure DrawFrame;
    function GetCircleGradient:TTeeGradient;
    Procedure InitCustom3DOptions; dynamic;
    Procedure PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                   var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}); override;
    Procedure SetActive(Value:Boolean); override;
    procedure SetCircleGradient(const Value: TTeeGradient);
    procedure SetCircleRect(const ARect:TRect);
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    Procedure SetParentProperties(EnableParentProps:Boolean); dynamic;
    Procedure SetRotationAngle(const Value:Integer);
    Function IsPointInChartRect(ValueIndex:Integer):Boolean; override;

    property CircleGradient:TTeeGradient read GetCircleGradient write SetCircleGradient;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    class procedure AdjustScreenRatio(const ACanvas:TCanvas3D;
                                      AXRadius,AYRadius:Integer;
                                      var R:TRect; isPrint : Boolean);

    Procedure AngleToPos( Const Angle,AXRadius,AYRadius:Double;
                          out X,Y:Integer); overload;

    Procedure AngleToPos( Const Angle,AXRadius,AYRadius:Double;
                          out X,Y:Single); overload;

    Function AssociatedToAxis(const Axis:TChartAxis):Boolean; override;

    // Returns angle from 0 to 2*Pi
    Function PointToAngle(x,y:Integer):Double;

    Function PointToRadius(x,y:Integer): Double;
    Procedure Rotate(const Angle:Integer);
    Function UseAxis:Boolean; override;

    { read only properties }
    property CircleBackColor:TColor read GetCircleBackColor
                                    write SetCircleBackColor default clTeeColor;
    property CircleBrush:TTeeBrush read FCircleBrush write SetCircleBrush;
    property CircleHeight:Integer read FCircleHeight;
    property CircleRect:TRect read FCircleRect write SetCircleRect;
    property CircleWidth:Integer read FCircleWidth;
    property CircleXCenter:Integer read FCircleXCenter;
    property CircleYCenter:Integer read FCircleYCenter;
    property RotationAngle:Integer read FRotationAngle write SetRotationAngle
                                   default 0;
    property XRadius:Integer read FXRadius;
    property YRadius:Integer read FYRadius;
  published
    property ColorSource;
    property Cursor;
    property Marks;
    property ParentChart;
    property DataSource;
    property PercentFormat;
    property SeriesColor;
    property ShowInLegend;
    property Title;
    property ValueFormat;
    property XLabelsSource;
    property XValues;
    property YValues;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    property Circled:Boolean read FCircled write SetCircled default True; // Was: False
    property CustomXRadius:Integer read FCustomXRadius write SetCustomXRadius default 0;
    property CustomYRadius:Integer read FCustomYRadius write SetCustomYRadius default 0;
    property Frame:TFramedBorder read GetFrame write SetFrame stored HasFrame;
    property UniqueCustomRadius:Boolean read FUniqueCustomRadius write SetUniqueCustomRadius default True;
    property Emboss:TTeeEmboss read FEmboss write SetEmboss;
    property Shadow:TTeeShadow read FShadow write SetShadow;
  end;

  TSliceValueList=class(TList{$IFDEF D18}<Integer>{$ENDIF})
  private
    Function Get(Index:Integer):Integer;
    Procedure Put(Index,Value:Integer);
  protected
    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    OwnerSeries : TChartSeries;
  public
    {$IFDEF D18}
    procedure Assign(const Source:TSliceValueList);
    {$ENDIF}

    {$IFNDEF D6}
    procedure Assign(const Source:TList);
    {$ENDIF}
    property Value[Index:Integer]:Integer read Get write Put; default;
  end;

  TPieOtherStyle=(poNone, poBelowPercent, poBelowValue);

  TPieOtherSlice=class(TPersistent)
  private
    FColor    : TColor;
    FLegend   : TChartLegend;
    FStyle    : TPieOtherStyle;
    FText     : String;
    FValue    : Double;

    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    FOwner    : TChartSeries;

    function GetLegend: TChartLegend;
    Function GetText:String;
    Function IsTextStored:Boolean;
    procedure SetColor(Value:TColor);
    procedure SetLegend(const Value: TChartLegend);
    procedure SetStyle(Value:TPieOtherStyle);
    procedure SetText(Const Value:String);
    procedure SetValue(Const Value:Double);
  public
    Constructor Create(const AOwner:TChartSeries);
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
  published
    property Color:TColor read FColor write SetColor default clTeeColor;
    property Legend:TChartLegend read GetLegend write SetLegend;
    property Style:TPieOtherStyle read FStyle write SetStyle default poNone;
    property Text:String read GetText write SetText stored IsTextStored;
    property Value:Double read FValue write SetValue;
  end;

  TRotateStyle=(rsRadial, rsTangencial);

  TPieMarks=class(TPersistent)
  private
    FEmptySlice : Boolean;
    FInsideSlice: Boolean;
    FLegSize    : Integer;
    FRotated    : Boolean;
    FRotateStyle: TRotateStyle;
    FVertCenter : Boolean;

    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    IParent : TChartSeries;

    procedure SetEmptySlice(const Value:Boolean);
    procedure SetInsideSlice(const Value:Boolean);
    procedure SetLegSize(const Value:Integer);
    procedure SetRotated(const Value:Boolean);
    procedure SetRotateStyle(const Value:TRotateStyle);
    procedure SetVertCenter(const Value:Boolean);
  public
    Procedure Assign(Source:TPersistent); override;
  published
    //property Alignment:  (left <-- --> right)
    property EmptySlice:Boolean read FEmptySlice write SetEmptySlice default False;
    property InsideSlice:Boolean read FInsideSlice write SetInsideSlice default False;
    property LegSize:Integer read FLegSize write SetLegSize default 0;
    property Rotated:Boolean read FRotated write SetRotated default False;
    property RotateStyle:TRotateStyle read FRotateStyle write SetRotateStyle default rsRadial;
    //property Underline:Boolean
    property VertCenter:Boolean read FVertCenter write SetVertCenter default False;
  end;

  TPieBevel=class(TPersistent)
  private
    FBright    : Integer;
    FPercent   : Integer;
    FUseBorder : Boolean;

    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}
    ISeries  : TChartSeries;

    procedure SetBright(const Value:Integer);
    procedure SetPercent(const Value:Integer);
    procedure SetUseBorder(const Value:Boolean);
  public
    Constructor Create(const AOwner:TChartSeries);

    procedure Assign(Source:TPersistent); override;
  published
    property Bright:Integer read FBright write SetBright default 32;
    property Percent:Integer read FPercent write SetPercent default 0;
    property UseBorder:Boolean read FUseBorder write SetUseBorder default True;
  end;

  TMultiPie=(mpAutomatic, mpDisabled, mpConcentric);

  TPieSeries=class(TCircledSeries)
  private
    FAngleSize        : Integer;
    FBevel            : TPieBevel;
    FCustomBounds     : TRect;
    FDark3D           : Boolean;
    FDarkPen          : Integer;
    FDonutPercent     : Integer;
    FEdgeStyle        : TEdgeStyle;
    FExplodedSlice    : TSliceValueList; { <-- Exploded slice % storage }
    FExplodeBiggest   : Integer;
    FGradientBright   : Integer;
    FMultiPie         : TMultiPie;
    FOtherSlice       : TPieOtherSlice;
    FPieMarks         : TPieMarks;
    FSliceHeights     : TSliceValueList; { <-- Slice Heights % storage }
    FUsePatterns      : Boolean;

    FAutoMarkPointer  : Boolean;

    ISortedSlice      : TIntArray;
    IOldChartRect     : TRect;
    IOldDonut         : Integer; // Concentric
    IHideSides        : Boolean; // Concentric

    Procedure CalcExplodeBiggest;
    function ChangeColor(const AColor:TColor; ABright:Integer):TColor;
    Function CompareSlice(A,B:Integer):Integer;
    Procedure DisableRotation;
    function GetAutoMarkPosition:Boolean;
    function GetBevelPercent:Integer;
    function GetMarkQuadrant(const Angle:Double; var Align:TTailAlign):TTailEdge;
    Function GetPiePen:TChartPen;
    Function GetPieValues:TChartValueList;
    Function GetTailEnd(const x, y, margin, zPos:Integer; const Align:TTailAlign):TPoint;
    function PieCount:Integer;
    Procedure PreparePiePen(ValueIndex:Integer);
    procedure RemoveOtherSlice;
    Procedure SetAngleSize(Value:Integer);
    Procedure SetAutoMarkPosition(Value:Boolean);
    Procedure SetBevel(const Value:TPieBevel);
    Procedure SetBevelPercent(Value:Integer);
    Procedure SetDark3D(Value:Boolean);
    procedure SetDarkPen(const Value: Integer);
    procedure SetEdgeStyle(Value:TEdgeStyle);
    procedure SetExplodeBiggest(Value:Integer);
    procedure SetGradientBright(Value:Integer);
    procedure SetGradientColor(const AGradient:TTeeGradient; AColor:TColor);
    procedure SetMultiPie(const Value:TMultiPie);
    procedure SetOtherSlice(Value:TPieOtherSlice);
    Procedure SetPieMarks(const Value:TPieMarks);
    Procedure SetPieValues(const Value:TChartValueList);
    procedure SetUsePatterns(Value:Boolean);
    Function SliceBrushStyle(ValueIndex:Integer):{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
    Function SliceEndZ(ValueIndex:Integer):Integer;
    Procedure SwapSlice(a,b:Integer);
  protected
    FAngles    : TPieAngles;
    IsExploded : Boolean;

    Procedure AddSampleValues(NumValues:Integer; Sequential:Boolean=False); override;
    Procedure AssignFormatting(Source:TChartSeries); override;
    Procedure CalcAngles;
    Procedure CalcExplodedOffset( ValueIndex:Integer;
                                  out OffsetX,OffsetY:Integer);
    Procedure CalcExplodedRadius(ValueIndex:Integer; out AXRadius,AYRadius:Integer);
    procedure CalcSelectionPos(ValueIndex:Integer; out X,Y:Integer); override;
    Function CalcLegendIndex(LegendIndex:Integer):Integer; override;
    Procedure CalcZOrder; override;
    procedure CheckAngles;
    Procedure CheckOtherSeriesMarks; override;
    Procedure ClearLists; override;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    procedure DoAfterDrawValues; override;
    procedure DoBeforeDrawChart; override;
    procedure DoBeforeDrawValues; override;
    procedure DrawAllValues; override;
    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;       
    procedure DrawMarks; override;
    Procedure DrawPie(ValueIndex:Integer); virtual;
    procedure DrawValue(ValueIndex:Integer); override;
    Procedure GalleryChanged3D(Is3D:Boolean); override;
    class Function GetEditorClass:String; override;
    Procedure InitCustom3DOptions; override;
    Function MoreSameZOrder:Boolean; override;
    Procedure PrepareForGallery; override;
    Procedure PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                   var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}); override;
    procedure SetDonutPercent(Value:Integer);
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
    procedure WriteData(Stream: TStream); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Function AddPie(Const AValue:Double; Const ALabel:String='';
                    AColor:TColor=clTeeColor):Integer;
    Function BelongsToOtherSlice(ValueIndex:Integer):Boolean;
    Function CalcClickedPie(x,y:Integer; Exploded:Boolean=True):Integer;
    Procedure CalcFirstLastVisibleIndex; override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Procedure CheckOrder; override;
    procedure Clear; override;
    Function Clicked(x,y:Integer):Integer; override;
    Function CountLegendItems:Integer; override;
    procedure Delete(ValueIndex:Integer); override;
    Function LegendToValueIndex(LegendIndex:Integer):Integer; override;
    Function MaxXValue:TChartValue; override;
    Function MinXValue:TChartValue; override;
    Function MaxYValue:TChartValue; override;
    Function MinYValue:TChartValue; override;
    Function NumSampleValues:Integer; override;
    procedure SwapValueIndex(a,b:Integer); override;

    property Angles:TPieAngles read FAngles;
    property DonutPercent:Integer read FDonutPercent write SetDonutPercent;
    property ExplodedSlice:TSliceValueList read FExplodedSlice;
    property SliceHeight:TSliceValueList read FSliceHeights;
  published
    property Active;
    property AngleSize:Integer read FAngleSize write SetAngleSize default 360;
    property AutoMarkPosition:Boolean read GetAutoMarkPosition write SetAutoMarkPosition stored False; // Deprecated. See Marks.AutoPosition
    property Bevel:TPieBevel read FBevel write SetBevel;
    property BevelPercent:Integer read GetBevelPercent write SetBevelPercent default 0;
    property CircleBackColor;
    property ColorEachPoint default True;
    property Dark3D:Boolean read FDark3D write SetDark3D default True;
    property DarkPen:Integer read FDarkPen write SetDarkPen default 0;
    property EdgeStyle:TEdgeStyle read FEdgeStyle write SetEdgeStyle default edNone;
    property ExplodeBiggest:Integer read FExplodeBiggest write SetExplodeBiggest default 0;
    property Gradient:TTeeGradient read GetCircleGradient write SetCircleGradient;
    property GradientBright:Integer read FGradientBright write SetGradientBright default 0;
    property MultiPie:TMultiPie read FMultiPie write SetMultiPie default mpAutomatic;
    property OtherSlice:TPieOtherSlice read FOtherSlice write SetOtherSlice;
    property PieMarks:TPieMarks read FPieMarks write SetPieMarks;
    property PiePen:TChartPen read GetPiePen write SetPen;
    property PieValues:TChartValueList read GetPieValues write SetPieValues stored False;
    property RotationAngle;
    property Transparency;
    property UsePatterns:Boolean read FUsePatterns write SetUsePatterns default False;
  end;

  TDrawAllPointsStyle=(daFirst,daMinMax);
  TFastLineDrawStyle=(flSegments,flAll);

  {$IFNDEF FMX}
  {.$DEFINE FASTLINEFLOAT} // Not working fine yet.
  {$ENDIF}

  TFastLineSeries=class(TCustomLineSeries)
  private
    FAutoRepaint    : Boolean;
    FClickTolerance : Integer;
    FDAPStyle       : TDrawAllPointsStyle;
    FDrawAll        : Boolean;
    FDrawStyle      : TFastLineDrawStyle;

    {$IFDEF TEEOCX}
    FExpandAxis     : Integer;
    {$ENDIF}

    FFastPen        : Boolean;
    FIgnoreNulls    : Boolean;
    FInvertedStairs : Boolean;
    FStairs         : Boolean;

    {$IFNDEF FMX}
    {$IFDEF MSWINDOWS}
    DCPEN        : HGDIOBJ;
    {$ENDIF}
    {$ENDIF}

    { internal }
    OldX         : Integer;
    OldY         : Integer;
    IMin         : Integer;
    IMax         : Integer;
    ISameX       : Boolean;
    IPoints      : {$IFDEF FASTLINEFLOAT}TPointFloatArray{$ELSE}TPointArray{$ENDIF};
    IDrawingAllValues : Boolean;

    procedure DoMove(const X,Y:Integer);
    Procedure SetDAPStyle(const Value:TDrawAllPointsStyle);
    Procedure SetDrawAll(const Value:Boolean);
    Procedure SetDrawStyle(const Value:TFastLineDrawStyle);
    procedure SetFastPen(const Value: Boolean);
    procedure SetIgnoreNulls(const Value: Boolean);
    procedure SetInvertedStairs(const Value: Boolean);
    procedure SetStairs(const Value: Boolean);
    function ShouldDrawAsPolyline:Boolean;
    function UseFastLine:Boolean;
  protected
    ReuseXValues,
    ReuseYValues : Boolean;

    procedure AssignFormatting(Source:TChartSeries); override;
    Procedure CalcHorizMargins(var LeftMargin,RightMargin:Integer); override;
    Procedure CalcPosition(ValueIndex:Integer; out x,y:Integer); {$IFDEF D9}inline;{$ENDIF}
    Procedure CalcVerticalMargins(var TopMargin,BottomMargin:Integer); override;
    function CanBeforeParallel:Boolean; override;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    procedure DoBeforeParallel; override;
    procedure DrawAllValues; override;
    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
    procedure DrawValue(ValueIndex:Integer); override;
    class Function GetEditorClass:String; override;
    Procedure NotifyNewValue(Sender:TChartSeries; ValueIndex:Integer); override;
    procedure PrepareCanvas;
    Procedure PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                   var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF}); override;
    Procedure SetPen(Const Value:TChartPen); override;
    Procedure SetSeriesColor(AColor:TColor); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Procedure Clear; override;

    Procedure Assign(Source:TPersistent); override;
    Procedure CalcFirstLastVisibleIndex; override;
    Function Clicked(x,y:Integer):Integer; override;
  published
    property Active;
    property Cursor;
    property Depth;
    property HorizAxis;
    property Marks;
    property ParentChart;
    property DataSource;
    property PercentFormat;
    property SeriesColor;
    property ShowInLegend;
    property Title;
    property ValueFormat;
    property VertAxis;
    property XLabelsSource;

    { events }
    property AfterDrawValues;
    property BeforeDrawValues;
    property OnAfterAdd;
    property OnBeforeAdd;
    property OnClearValues;
    property OnClick;
    property OnDblClick;
    property OnGetMarkText;
    property OnMouseEnter;
    property OnMouseLeave;

    property AutoRepaint:Boolean read FAutoRepaint write FAutoRepaint default True;

    property ClickTolerance:Integer read FClickTolerance write FClickTolerance default 0;

    {$IFDEF TEEOCX}
    property ExpandAxis:Integer read FExpandAxis write FExpandAxis default 25;
    {$ENDIF}

    property DrawAllPoints:Boolean read FDrawAll write SetDrawAll default True;
    property DrawAllPointsStyle:TDrawAllPointsStyle read FDAPStyle write SetDAPStyle default daFirst;

    property DrawStyle:TFastLineDrawStyle read FDrawStyle write SetDrawStyle default flSegments;

    property FastPen:Boolean read FFastPen write SetFastPen default False;

    // obsolete and redundant, please use TreatNulls property:
    property IgnoreNulls:Boolean read FIgnoreNulls write SetIgnoreNulls default True;

    property InvertedStairs:Boolean read FInvertedStairs write SetInvertedStairs default False;
    property LinePen;
    property Stairs:Boolean read FStairs write SetStairs default False;
    property TreatNulls default tnIgnore;
    property XValues;
    property YValues;
  end;

Const
  bsCylinder=bsCilinder;  { <-- better spelling... }

// Calls RegisterTeeSeries for each "standard" series type:
// Line, Bar, Pie, FastLine, HorizBar, Area, Point and HorizLine
Procedure RegisterTeeStandardSeries;

Procedure TeePointerDrawLegend(const Pointer:TSeriesPointer; AColor:TColor;
                               Const Rect:TRect; DrawPen:Boolean;
                               AStyle:TSeriesPointerStyle);


implementation

uses
  {$IFDEF FMX}
  FMX.Platform,
  {$ENDIF}
  Math,

  {$IFDEF LCL}
  Types,
  {$ENDIF}

  {$IFDEF FMX}
  FMXTee.Constants
  {$ELSE}
  TeeConst
  {$ENDIF};

{$IFNDEF FMX}
Function GetDefaultPattern(const PatternIndex:Integer):TBrushStyle;
Const
  MaxDefaultPatterns = 6;

  PatternPalette     : Array[1..MaxDefaultPatterns] of TBrushStyle=
      ( bsHorizontal,
        bsVertical,
        bsFDiagonal,
        bsBDiagonal,
        bsCross,
        bsDiagCross
      );
Begin
  result:=PatternPalette[1+(PatternIndex mod MaxDefaultPatterns)];
End;
{$ENDIF}

Procedure TeePointerDrawLegend(const Pointer:TSeriesPointer; AColor:TColor;
                               const Rect:TRect; DrawPen:Boolean;
                               AStyle:TSeriesPointerStyle);
var tmpHoriz : Integer;
    tmpVert  : Integer;
    tmpPenW  : Integer;
    tmpX     : Integer;
    tmpY     : Integer;
    tmp      : Integer;
    tmpAspect : Single;
    Old      : TPointerSizeUnits;
begin
  if Assigned(Pointer.ParentChart) then
  begin
    Pointer.PrepareCanvas(Pointer.ParentChart.Canvas,AColor);

    if Pointer.Pen.Visible then
       tmpPenW:={$IFDEF FMX}Round{$ENDIF}(Pointer.Pen.Width)
    else
       tmpPenW:=0;

    with TCustomChart(Pointer.ParentChart) do
    if not Legend.Symbol.DefaultPen then
    begin
      Canvas.AssignVisiblePen(Legend.Symbol.Pen); { use custom legend pen }
      DrawPen:=Legend.Symbol.Pen.Visible;
      tmpPenW:={$IFDEF FMX}Round{$ENDIF}(Legend.Symbol.Pen.Width);
    end;

    with Rect{$IFDEF FMX}.Round{$ENDIF} do
    begin
      if DrawPen then
      begin
        tmpHoriz:=(Right-Left);
        tmpVert:=(Bottom-Top);

        if (2*tmpPenW>tmpHoriz) or (2*tmpPenW>tmpVert) then
           Pointer.ParentChart.Canvas.Pen.Width:=Min(tmpHoriz,tmpVert) div 2;

        tmpHoriz:=tmpHoriz div 2;
        tmpVert :=tmpVert div 2;
      end
      else
      begin
        tmpHoriz:=1+((Right-Left) div 2);
        tmpVert :=1+((Bottom-Top) div 2);
      end;

      tmpX:=(Left+Right) div 2;
      tmpY:=(Top+Bottom) div 2;

      if Pointer.Shadow.Visible then
      begin
        tmp:=Pointer.Shadow.HorizSize div 2;
        Dec(tmpHoriz,tmp);
        Dec(tmpX,tmp);
      end;

      // http://bugs.teechart.net/show_bug.cgi?id=580
      if not (Pointer.ParentChart as TCustomChart).Legend.Symbol.Continuous then
      begin
        if Pointer.HorizSize=0 then
           tmpAspect:=1
        else
           tmpAspect:=Pointer.VertSize/Pointer.HorizSize;

        if tmpVert*tmpAspect>tmpHoriz then
           tmpVert:=Min(tmpVert,Round(tmpHoriz*tmpAspect))
        else
           tmpHoriz:=Min(tmpHoriz,Round(tmpVert/tmpAspect));

        tmpHoriz:=Min(tmpHoriz,Pointer.HorizSize);
        tmpVert:=Min(tmpVert,Pointer.VertSize);
      end;

      Old:=Pointer.SizeUnits;
      Pointer.SizeUnits:=suPixels;

      Pointer.DrawPointer(Pointer.ParentChart.Canvas,
                          False,
                          tmpX,
                          tmpY,
                          tmpHoriz,
                          tmpVert,
                          AColor,AStyle);

      Pointer.SizeUnits:=Old;
    end;
  end;
end;

{ TCustomLineSeries }

Function TCustomLineSeries.GetLinePen:TChartPen;
Begin
  result:=Pen;
end;

{ TSeriesPointerItems }

Destructor TSeriesPointerItems.Destroy;
begin
  Clear;
  inherited;
end;

procedure TSeriesPointerItems.Delete(Index:Integer);
begin
  if Count>Index then
  begin
    TSeriesPointer(IList[Index]).{$IFDEF D18}DisposeOf{$ELSE}Free{$ENDIF};
    IList.Delete(Index);
  end;
end;

procedure TSeriesPointerItems.Ensure(Index:Integer);
begin
  if not Assigned(IList) then
     IList:=TList{$IFDEF D18}<TSeriesPointer>{$ENDIF}.Create;

  while Count-1<Index do
        IList.Add(nil);
end;

function TSeriesPointerItems.Get(Index:Integer):TSeriesPointer;
begin
  Ensure(Index);
  result:=TSeriesPointer(IList[Index]);

  if not Assigned(result) then
  begin
    result:=TSeriesPointer.Create(ParentSeries);
    result.Assign(Self);
    IList[Index]:=result;
  end;
end;

procedure TSeriesPointerItems.Clear;
var t : Integer;
begin
  if Assigned(IList) then
  begin
    for t:=0 to IList.Count-1 do
        TSeriesPointer(IList[t]).{$IFDEF D18}DisposeOf{$ELSE}Free{$ENDIF};

    FreeAndNil(IList);
  end;

  Repaint;
end;

function TSeriesPointerItems.Count:Integer;
begin
  if Assigned(IList) then
     result:=IList.Count
  else
     result:=0;
end;

procedure TSeriesPointerItems.InternalPut(Index:Integer; const Value:TSeriesPointer);
begin
  if Assigned(IList[Index]) then
     TSeriesPointer(IList[Index]).{$IFDEF D18}DisposeOf{$ELSE}Free{$ENDIF};

  IList[Index]:=Value;
end;

procedure TSeriesPointerItems.Put(Index:Integer; const Value:TSeriesPointer);
begin
  if Assigned(Value) then
  begin
    Ensure(Index);
    InternalPut(Index,Value);
  end
  else
  if IList.Count>Index then
     InternalPut(Index,nil);

  Repaint;
end;

function TSeriesPointerItems.InternalGet(Index:Integer):TSeriesPointer;
begin
  if (Index>=0) and Assigned(IList) and (IList.Count>Index) then
  begin
    result:=TSeriesPointer(IList[Index]);

    if not Assigned(result) then
       result:=Self;
  end
  else
    result:=Self;
end;

{ TCustomSeries }

Constructor TCustomSeries.Create(AOwner: TComponent);
Begin
  inherited;

  FClickableLine:=True;
  FColorEachLine:=True;

  DisplayLast:=1;

  FPointer:=TSeriesPointerItems.Create(Self);
  HoverElement:=[heCurrent];

  FAreaLinesPen:=CreateChartPen;

  FEmboss:=TTeeEmboss.Create(CanvasChanged);
  FShadow:=TTeeShadow.Create(CanvasChanged);

  FAreaBrush:=TTeeBrush.Create(CanvasChanged);
  FAreaColor:=clTeeColor;

  AreaChartBrush.Color:={$IFDEF FMX}TAlphaColors.Gray{$ELSE}clGray{$ENDIF}; //TV52015167
  AreaChartBrush.BackColor:=FAreaColor;

  Brush.Color:=FAreaColor;
  Brush.BackColor:=FAreaColor;
  Brush.OnChange:=ChangedBrush;

  FDark3D:=True;
  FInflateMargins:=True;
end;

procedure TCustomSeries.ChangedBrush(Sender: TObject);
Begin
  if (Sender is TBrush) and (not (Sender is TChartBrush)) then
    Color:=Brush.Color;

  CanvasChanged(Sender);
End;

Procedure TCustomSeries.Delete(ValueIndex:Integer);
begin
  inherited;
  Pointer.Delete(ValueIndex);
end;

procedure TCustomSeries.SwapValueIndex(a,b:Integer);
var tmp : TObject;
begin
  inherited;

  if Pointer.Count>Max(a,b) then
  begin
    tmp:=Pointer.IList[a];
    Pointer.IList[a]:=Pointer.IList[b];
    Pointer.IList[b]:={$IFDEF D18}TSeriesPointer{$ENDIF}(tmp);
  end;
end;

Destructor TCustomSeries.Destroy;
Begin
  FAreaBrush.Free;
  FAreaLinesPen.Free;
  FEmboss.Free;
  FShadow.Free;
  FOutLine.Free;
  FreeAndNil(FPointer);

  {$IFNDEF TEELITEEMB}
  FreeSmoothSeries;
  {$ENDIF}
  
  inherited;
end;

Procedure TCustomSeries.DrawMark( ValueIndex:Integer; Const St:String;
                                  APosition:TSeriesMarkPosition);
begin
  Marks.ZPosition:=StartZ;

  if YMandatory then
     Marks.ApplyArrowLength(APosition);

  inherited;
end;

Procedure TCustomSeries.DoBeforeDrawChart;
begin
  inherited;

  {$IFNDEF TEELITEEMB}
  SmoothSeries;
  {$ENDIF}
end;

function TCustomSeries.DoGetPointerStyle(ValueIndex:Integer):TSeriesPointerStyle;
begin
  if Assigned(FOnGetPointerStyle) then
     result:=FOnGetPointerStyle(Self,ValueIndex)
  else
     result:=Pointer.InternalGet(ValueIndex).Style;
end;

type
  TPointerAccess=class(TSeriesPointer);

Function TCustomSeries.ClickedPointer( ValueIndex,tmpX,tmpY:Integer;
                                       x,y:Integer):Boolean;

var tmpHoriz : Integer;
    tmpVert  : Integer;
begin
  result:=DoGetPointerStyle(ValueIndex)<>psNothing;

  if result then
  begin
    PreparePointer(ValueIndex);

    TPointerAccess(FPointer).CalcSize(tmpHoriz,tmpVert);

    result:=(Abs(tmpX-X)<(tmpHoriz+ClickTolerance)) and
            (Abs(tmpY-Y)<(tmpVert+ClickTolerance));

    if result then
       IClickedPart:=scpPointer;
  end;
end;

Procedure TCustomSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TCustomSeries then
  With TCustomSeries(Source) do
  begin
    Self.AreaChartBrush  :=FAreaBrush;
    Self.FAreaColor      :=FAreaColor;
    Self.AreaLinesPen    :=AreaLinesPen;
    Self.FColorEachLine  :=ColorEachLine;
    Self.FDark3D         :=FDark3D;
    Self.FDrawArea       :=FDrawArea;
    Self.FDrawLine       :=FDrawLine;
    Self.FInflateMargins :=FInflateMargins;
    Self.FInvertedStairs :=FInvertedStairs;
    Self.FLineHeight     :=FLineHeight;
    Self.Pointer         :=FPointer;
    Self.FPointerBehind  :=FPointerBehind;
    Self.FStairs         :=FStairs;
    Self.OutLine         :=FOutLine;
    Self.Emboss          :=Emboss;
    Self.Shadow          :=Shadow;
  end;

  inherited;
end;

Procedure TCustomSeries.Assign(Source:TPersistent);
begin
  if Source is TCustomSeries then
  With TCustomSeries(Source) do
  begin
    Self.ClickableLine :=ClickableLine;
    Self.ClickTolerance:=ClickTolerance;
    Self.FDrawStyle    :=FDrawStyle;
    Self.FStacked      :=FStacked;
  end;

  inherited;
end;

Function TCustomSeries.Clicked(x,y:Integer):Integer;
var OldXPos  : Integer;
    OldYPos  : Integer;
    tmpX     : Integer;
    tmpY     : Integer;
    P        : TPoint;

  Function CheckPointInLine:Boolean;

    Function PointInVertLine(x0,y0,y1:Integer):Boolean;
    begin
      result:=PointInLine(P,x0,y0,x0,y1,ClickTolerance);
    end;

    Function PointInHorizLine(x0,y0,x1:Integer):Boolean;
    begin
      result:=PointInLine(P,x0,y0,x1,y0,ClickTolerance);
    end;

  begin
    With ParentChart do
    if View3D then
       result:=PointInPolygon( P,[ TeePoint(tmpX,tmpY),
                                   TeePoint(tmpX+SeriesWidth3D,tmpY-SeriesHeight3D),
                                   TeePoint(OldXPos+SeriesWidth3D,OldYPos-SeriesHeight3D),
                                   TeePoint(OldXPos,OldYPos) ])
    else
       if FStairs then
       begin
          if FInvertedStairs then result:= PointInVertLine(OldXPos,OldYPos,tmpY) or
                                           PointInHorizLine(OldXPos,tmpY,tmpX)
                             else result:= PointInHorizLine(OldXPos,OldYPos,tmpX) or
                                           PointInVertLine(tmpX,OldYPos,tmpY);
       end
       else
          result:=PointInLine(P,tmpX,tmpY,OldXPos,OldYPos,ClickTolerance);

    if result then
       IClickedPart:=scpSegment;
  end;

  function PointInArea(Index:Integer):Boolean; // TV52012421
  var tmp : TFourPoints;
  begin
    if YMandatory then
    begin
      tmp[0].X:=OldXPos;
      tmp[1].X:=tmpX;
      tmp[2]:=TeePoint(tmpX,GetOriginPos(Index));
      tmp[3]:=TeePoint(OldXPos,OldBottomPos);

      if FStairs then
      begin
         if FInvertedStairs then
         begin
           tmp[0].Y:=tmpY;
           tmp[1].Y:=tmpY;
           tmp[3].Y:=tmp[2].Y;
         end
         else
         begin
           tmp[0].Y:=OldYPos;
           tmp[1].Y:=OldYPos;
           tmp[2].Y:=tmp[3].Y;
         end;
      end
      else
      begin
        tmp[0].Y:=OldYPos;
        tmp[1].Y:=tmpY;
      end;
    end
    else
    begin
      tmp[0].Y:=OldYPos;
      tmp[1].Y:=tmpY;
      tmp[2]:=TeePoint(GetOriginPos(Index),tmpY);
      tmp[3]:=TeePoint(OldBottomPos,OldYPos);

      if FStairs then
         if FInvertedStairs then
         begin
           tmp[0].X:=tmpX;
           tmp[1].X:=tmpX;
         end
         else
         begin
           tmp[0].X:=OldXPos;
           tmp[1].X:=OldXPos;
         end
      else
      begin
        tmp[0].X:=OldXPos;
        tmp[1].X:=tmpX;
      end;
    end;

    result:=PointInPolygon(P,tmp);

    if result then
       IClickedPart:=scpSegment;
  end;

var
  tmpFirst : Integer;
  tmpAxis  : TChartAxis;

  function ClickedPoint(Index:Integer):Integer;
  begin
    result:=TeeNoPointClicked;
    Pointer.Selected.HoverIndex:=Result;

    if Index>=Count then
       Exit;  // prevent re-entrancy if series is cleared.

    tmpX:=CalcXPos(Index);
    tmpY:=CalcYPos(Index);

    if FPointer.Visible and ClickedPointer(Index,tmpX,tmpY,x,y) then
    begin
      result:=Index;
      Pointer.Selected.HoverIndex:=Result;
    end
    else
    if (tmpX=X) and (tmpY=Y) then
    begin
      result:=Index;
      IClickedPart:=scpSegment;
    end
    else
    if (Index>=tmpFirst) and ClickableLine then
      if CheckPointInLine or (FDrawArea and PointInArea(Index)) then
        if tmpAxis.Inverted then
           result:=Index
        else
           result:=Pred(Index);

    OldXPos:=tmpX;
    OldYPos:=tmpY;
    OldBottomPos:=BottomPos;
  end;

var t : Integer;
begin
  if Assigned(ParentChart) then
     ParentChart.Canvas.Calculate2DPosition(X,Y,StartZ);

  if not PointInRect(TeeRect(GetHorizAxis.IStartPos, GetVertAxis.IStartPos, GetHorizAxis.IEndPos, GetVertAxis.IEndPos), x, y) then
  begin
    result:=TeeNoPointClicked;
    Exit;
  end;

  result:=inherited Clicked(x,y);

  if (result=TeeNoPointClicked) and
     (FirstValueIndex>-1) and (LastValueIndex>-1) then
  begin
    OldXPos:=0;
    OldYPos:=0;
    OldBottomPos:=0;
    P.X:=X;
    P.Y:=Y;

    // Consider hidden previous segment (including non-visible leftmost part)
    // TV52010286
    if ClickableLine then
       tmpFirst:=Max(0,Pred(FirstValueIndex))
    else
       tmpFirst:=FirstValueIndex;

    if YMandatory then
       tmpAxis:=GetHorizAxis
    else
       tmpAxis:=GetVertAxis;

    if tmpAxis.Inverted then
    begin
       for t:=LastValueIndex downto tmpFirst do
       begin
         result:=ClickedPoint(t);

         if result<>TeeNoPointClicked then
            break;
       end;
    end
    else
       for t:=tmpFirst to LastValueIndex do
       begin
         result:=ClickedPoint(t);

         if result<>TeeNoPointClicked then
            break;
       end;
  end;
end;

Procedure TCustomSeries.SetDrawArea(Value:Boolean);
Begin
  SetBooleanProperty(FDrawArea,Value);
end;

procedure TCustomSeries.SetDrawStyle(const Value:TCustomSeriesDrawStyle);
begin
  if FDrawStyle<>Value then
  begin
    FDrawStyle:=Value;
    Repaint;
  end;
end;

Procedure TCustomSeries.SetPointer(const Value:TSeriesPointerItems);
Begin
  FPointer.Assign(Value);
end;

Procedure TCustomSeries.SetPointerBehind(const Value:Boolean);
begin
  SetBooleanProperty(FPointerBehind,Value);
end;

Procedure TCustomSeries.SetShadow(const Value:TTeeShadow);
begin
  FShadow.Assign(Value);
end;

Procedure TCustomSeries.SetEmboss(const Value:TTeeEmboss);
begin
  FEmboss.Assign(Value);
end;

Procedure TCustomSeries.SetAreaLinesPen(const Value:TChartPen);
Begin
  FAreaLinesPen.Assign(Value);
end;

Procedure TCustomSeries.SetLineHeight(Value:Integer);
Begin
  SetIntegerProperty(FLineHeight,Value);
end;

Procedure TCustomSeries.SetStairs(Value:Boolean);
Begin
  SetBooleanProperty(FStairs,Value);
end;

{$IFDEF TEEOCX}
Procedure TCustomSeries.SetFastPoint(Value:Boolean);
Begin
  SetBooleanProperty(FFastPoint,Value);
end;
{$ENDIF}

Procedure TCustomSeries.SetInflateMargins(Value:Boolean);
begin
  SetBooleanProperty(FInflateMargins,Value);
end;

Procedure TCustomSeries.SetInvertedStairs(Value:Boolean);
begin
  SetBooleanProperty(FInvertedStairs,Value);
end;

Procedure TCustomSeries.SetAreaColor(Value:TColor);
begin
  SetColorProperty(FAreaColor,Value);
end;

Procedure TCustomSeries.SetAreaBrushStyle(const Value:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
begin
  FAreaBrush.Style:=Value;
end;

Function TCustomSeries.GetLineBrush:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
begin
  result:=Brush.Style;
end;

Procedure TCustomSeries.SetLineBrush(const Value:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
begin
  Brush.Style:=Value;
end;

Procedure TCustomSeries.DrawLegendShape(ValueIndex:Integer; Const Rect:TRect);
var tmpColor : TColor;

  Procedure DrawLine(DrawRectangle:Boolean);
  var tmpW : Integer;
      tmpBlend : TTeeBlend;
  begin
    if Transparency>0 then
       tmpBlend:=ParentChart.Canvas.BeginBlending(Rect,Transparency)
    else
       tmpBlend:=nil;

    if not PrepareHoverPen(ValueIndex) then
      if TCustomChart(ParentChart).Legend.Symbol.DefaultPen then
         if Pen.Color <> {$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF} then
            LinePrepareCanvas(ParentChart.Canvas,Pen.Color)
         else
            LinePrepareCanvas(ParentChart.Canvas,tmpColor);

    with ParentChart.Canvas do
    begin
      // Check pen width overflow:
      tmpW:={$IFDEF FMX}Round{$ENDIF}((Rect.Bottom-Rect.Top) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});
      if Pen.Width>tmpW then
         Pen.Width:=tmpW;

      if DrawRectangle then
         Rectangle(Rect)
      else
      with Rect{$IFDEF FMX}.Round{$ENDIF} do
           DoHorizLine(Left,Right,(Top+Bottom) div 2);
    end;

    if Transparency>0 then
       ParentChart.Canvas.EndBlending(tmpBlend);
  end;

var
  tmpItem : TSeriesPointer;

  procedure DoDrawPointer;
  var tmp : TSeriesPointerStyle;
      tmpVisual : TVisualBlock;
  begin
    if Assigned(FOnGetPointerStyle) and (ValueIndex<>TeeAllValues) then
    begin
      tmp:=FOnGetPointerStyle(Self,ValueIndex);
      {$IFNDEF TEEOCX}
      tmpColor:=tmpItem.Color;
      {$ENDIF}
    end
    else
       tmp:=tmpItem.Style;

    if tmp=psVisual then
       tmpVisual:=Visuals[ValueIndex]
    else
       tmpVisual:=nil;

    if not Assigned(tmpVisual) then
       TeePointerDrawLegend(tmpItem,tmpColor,Rect,LinePen.Visible,tmp);
  end;

begin
  if ValueIndex=TeeAllValues then tmpColor:=SeriesColor
                             else tmpColor:=LegendItemColor(ValueIndex);

  tmpItem:=Pointer.InternalGet(ValueIndex);

  if tmpItem.Visible then
  begin
    if PointerBehind then
       DoDrawPointer;

    if FDrawLine then
       DrawLine(False);

    if not PointerBehind then
       DoDrawPointer;
  end
  else
  if FDrawLine and (not FDrawArea) then
     DrawLine(ParentChart.View3D)
  else
  begin
    PrepareHoverPen(ValueIndex);

    inherited;
  end;
end;

procedure TCustomSeries.LinePrepareCanvas(const ACanvas:TCanvas3D; tmpColor:TColor);
var Old : Boolean;
begin
  if ACanvas.MonoChrome then
     tmpColor:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};

  if ParentChart.View3D then
  begin
    if Brush.Image.Valid then
       {$IFDEF FMX}
       ACanvas.Brush.Bitmap.Bitmap.Assign(Brush.Image.Bitmap)
       {$ELSE}
       ACanvas.Brush.Bitmap:=Brush.Image.Bitmap
       {$ENDIF}
    else
    begin
      ACanvas.Brush.Style:=LineBrush;
      ACanvas.Brush.Color:=tmpColor;
    end;

    ACanvas.AssignVisiblePen(LinePen);
  end
  else
  begin
    ACanvas.Brush.Style:=bsClear;

    Old:=LinePen.Visible;

    // Force 2D visible pen in this case, otherwise nothing will be painted:
    if (not DrawArea) and (not Pointer.Visible) then
       LinePen.Visible:=True;

    ACanvas.AssignVisiblePenColor(LinePen,tmpColor);

    LinePen.Visible:=Old;
  end;
end;

procedure TCustomSeries.MouseDown(ValueIndex:Integer;  AtPointer:Boolean;
                                  Button:TMouseButton;
                                  Shift: TShiftState; X, Y: Integer);
begin
  inherited;

  // Fixes TV52014945 and TV52015075
  if AtPointer and (not ParentChart.CancelMouse) then
     if Assigned(FOnClickPointer) then
         FOnClickPointer(Self,ValueIndex,x,y);
end;

procedure TCustomSeries.PreparePointer(ValueIndex:Integer);
begin  // empty. Overriden at BubbleSeries
end;

function TCustomSeries.PreparePointerHoverPen(ValueIndex:Integer):Boolean;

  function IsNext: Boolean;
  begin
    result:=(heNext in HoverElement) and (Selected.HoverIndex=Pred(ValueIndex));
  end;

  function IsPrevious: Boolean;
  begin
    result:=(hePrevious in HoverElement) and (Selected.HoverIndex=ValueIndex);
  end;

begin
  result:=HoverPointerEnabled and (HoverOverSegment<>-1) and (IsNext or IsPrevious);
  result:=result or (HoverPointerEnabled and (HoverOverSegment=-1) and (heCurrent in HoverElement) and
          ((HoverOverPointer=ValueIndex) or ((HoverOverMark=ValueIndex) and (heJoin in HoverElement))));

  if result then
     ParentChart.Canvas.AssignVisiblePen(Pointer.Selected.Hover.Pen)
end;

function TCustomSeries.PrepareHoverPen(ValueIndex:Integer):Boolean;
var tmpIndex: Integer;

  function IsNext: Boolean;
  begin
    result:=(heNext in HoverElement);

    if IClickedPart=scpPointer then
       result:=result and (Pointer.Selected.HoverIndex=tmpIndex)
    else
       result:=result and (Selected.HoverIndex=tmpIndex);
  end;

  function IsPrevious: Boolean;
  begin
    result:=(hePrevious in HoverElement);

    if IClickedPart=scpPointer then
       result:=result and (Pointer.Selected.HoverIndex=Succ(tmpIndex))
    else
       result:=result and (Selected.HoverIndex=Succ(tmpIndex));
  end;

var HoverPenEnabled: Boolean;
begin
  tmpIndex:=ValueIndex;

  if YMandatory then
  begin
    if GetHorizAxis.Inverted then
       tmpIndex:=Succ(tmpIndex);
  end
  else
    if GetVertAxis.Inverted then
       tmpIndex:=Pred(tmpIndex);

  HoverPenEnabled:=(not Assigned(FParent)) or FParent.Hover.Visible;
  HoverPenEnabled:=HoverPenEnabled and Selected.Hover.Visible;
  result:=HoverPenEnabled and ((HoverOverPointer<>-1) or (HoverOverMark<>-1)) and (IsNext or IsPrevious);
  result:=result or (HoverPenEnabled and (HoverOverPointer=-1) and (tmpIndex<>-1) and
          (HoverOverSegment=tmpIndex) and (heCurrent in HoverElement));

  if result then
     ParentChart.Canvas.AssignVisiblePen(Selected.Hover.Pen);
end;

function TCustomSeries.HoverEnabled:Boolean;
begin
  result:=(not Assigned(FParent)) or FParent.Hover.Visible;
  result:=result and (Selected.Hover.Visible or Pointer.Selected.Hover.Visible);
end;

function TCustomSeries.HoverPointerEnabled:Boolean;
begin
  result:=(not Assigned(FParent)) or FParent.Hover.Visible;
  result:=result and Pointer.Selected.Hover.Visible;
end;

function TCustomSeries.HoverOverPointer:Integer;
begin
  if IClickedPart=scpPointer then
     result:=Pointer.Selected.HoverIndex
  else
     result:=-1;
end;

function TCustomSeries.ShouldHoverMark(ValueIndex:Integer):Boolean;
begin
  result:=inherited ShouldHoverMark(ValueIndex) or ((HoverOverPointer=ValueIndex) and
          (heCurrent in HoverElement) and (heJoin in HoverElement));
end;

function TCustomSeries.RaiseClicked:Boolean;
begin
  result:=(inherited RaiseClicked) or
          (Pointer.Visible and Assigned(FOnClickPointer));
end;

procedure TCustomSeries.ResetBrush;
begin
  Brush.Style:=bsSolid;
  Brush.Color:=clTeeColor;
  Brush.BackColor:=clTeeColor;

  SetDefaultColor;
  Repaint;
end;

Procedure TCustomSeries.SetParentChart(Const Value:TCustomAxisPanel);
begin
  inherited;
  if Assigned(FPointer) then FPointer.ParentChart:=Value;
end;

{$IFNDEF TEELITEEMB}
procedure TCustomSeries.FreeSmoothSeries;
begin
  if Assigned(FSmoothSeries) then
     if (not (csDestroying in ComponentState)) and
        (not (csDestroying in FSmoothSeries.ComponentState)) then
     begin
       FSmoothSeries.Clear;
       FreeAndNil(FSmoothSeries);
     end;
end;

Procedure TCustomSeries.SetSmoothed(const Value:Boolean);
begin
  SetBooleanProperty(FSmoothed, Value);
end;
{$ENDIF}

Procedure TCustomSeries.Clear;
begin
  inherited;
  FDirty:=True;
end;

{$IFNDEF TEELITEEMB}
procedure TCustomSeries.SmoothSeries;
begin
  if DrawArea or FDrawLine then
    if FSmoothed then
    begin
      if not Assigned(FSmoothSeries) then
      begin
        FSmoothSeries:=TCustomSeries.Create(Self);
        FSmoothSeries.IInternalUse:=True;
        FSmoothSeries.ShowInEditor:=False;
        FSmoothSeries.ShowInLegend:=False;

        if not YMandatory then
           FSmoothSeries.SetHorizontal;

        if (FSmoothSeries.Count=0) or FDirty then
        begin
          FSmoothSeries.XValues.Assign(XValues);
          FSmoothSeries.XValues.Value:=XValues.Value;
          FSmoothSeries.XValues.Count:=Count;

          FSmoothSeries.YValues.Assign(YValues);
          FSmoothSeries.YValues.Value:=YValues.Value;
          FSmoothSeries.YValues.Count:=Count;
        end;

        if not Assigned(FunctionType) or FDirty then //To avoid re-entrancy
        begin
          FSmoothFunction:=TSmoothingFunction.Create(ParentChart);
          FSmoothFunction.Interpolate:=True;
          FSmoothFunction.Factor:=8;

          SetFunction(FSmoothFunction);

          OldDataSource:=DataSource;
          DataSource:=FSmoothSeries;

          FDirty:=False;
        end;
      end;
    end
    else
    if Assigned(FSmoothSeries) then
        if Count > FSmoothSeries.Count then // To avoid re-entrancy
        begin
          FunctionType:=nil;
          DataSource:=OldDataSource;

          if Assigned(DataSource) then
             CheckDataSource
          else
          begin
            XValues.Assign(FSmoothSeries.XValues);
            XValues.Value:=FSmoothSeries.XValues.Value;
            XValues.Count:=FSmoothSeries.Count;
            XValues.Modified:=True;

            YValues.Assign(FSmoothSeries.YValues);
            YValues.Value:=FSmoothSeries.YValues.Value;
            YValues.Count:=FSmoothSeries.Count;
            YValues.Modified:=True;
          end;

          FreeSmoothSeries;
          RefreshSeries;
        end;
end;
{$ENDIF}

Function TCustomSeries.GetAreaBrushColor(AColor:TColor):TColor;
begin
  if ColorEachPoint or (FAreaColor=clTeeColor) then
     result:=AColor
  else
     result:=FAreaColor;
end;

// Calculate pixel position of coordinate that is outside the axis limits.
// Called when a line point is outside the axis range, to avoid pixel coordinates
// overflow max limits (+- 32767 in Windows XP GDI)
function TCustomSeries.CalcPosLeftRight(Const ALimit:Double; ValueIndex,AnotherIndex:Integer):Integer;
var tmpPredValue : Double;
    tmpPredOther : Double;
    tmpDif       : Double;
    tmpValue     : Double;
    tmpNoMand    : TChartValues;
    tmpMand      : TChartValues;
begin
  if YMandatory then
     tmpNoMand:=XValues.Value
  else
     tmpNoMand:=YValues.Value;

  tmpPredValue:=tmpNoMand[AnotherIndex];
  tmpDif:=tmpNoMand[ValueIndex]-tmpPredValue;

  {$IFNDEF D7}
  tmpMand:=nil; // Just to skip compiler warning ("not initialized")
  {$ENDIF}

  if tmpDif=0 then
     if YMandatory then
        result:=CalcYPos(AnotherIndex)
     else
        result:=CalcXPos(AnotherIndex)
  else
  begin
    tmpMand:=MandatoryValueList.Value;

    tmpPredOther:=tmpMand[AnotherIndex];

    if MandatoryAxis.Logarithmic then
        if (tmpPredOther>0) and (tmpMand[ValueIndex]>0) then
           tmpValue:=Exp(Ln(tmpPredOther)+(ALimit-tmpPredValue)*
                        (Ln(tmpMand[ValueIndex])-Ln(tmpPredOther))/tmpDif)
        else
           tmpValue:=0 // TV52010688
    else
        tmpValue:=1.0*tmpPredOther+(ALimit-tmpPredValue)*
                 (tmpMand[ValueIndex]-tmpPredOther)/tmpDif;

    if YMandatory then
       result:=CalcYPosValue(tmpValue)
    else
       result:=CalcXPosValue(tmpValue);
  end;
end;

const
  TeeMaxPixelPosLine:Integer=$7FFF;

type
  TVisualAccess=class(TVisualBlock);

type
  TCanvasAccess=class(TTeeCanvas);

procedure TCustomSeries.DrawValue(ValueIndex:Integer);
var x : Integer;
    y : Integer;
    PreviousValueIndex: Integer;

var tmpColor    : TColor;
    IsLastValue : Boolean;

   Procedure InternalDrawArea(BrushColor:TColor; const ACanvas:TCanvas3D);

     function CalcBlend(const R:TRect):TTeeBlend; overload;
     begin
       if Transparency>0 then
          if ACanvas.SupportsAlpha then
             result:=ACanvas.BeginBlending(TeeZeroRect,Transparency)
          else
            if ParentChart.View3D then
               result:=ACanvas.BeginBlending(ACanvas.RectFromRectZ(R,StartZ),Transparency)
            else
               result:=ACanvas.BeginBlending(R,Transparency)
       else
          result:=nil;
     end;

     function CalcBlend(const P:TFourPoints):TTeeBlend; overload;

       // Calculates the bounding rectangle of the "P" four points polygon.
       // Called only for "old GDI", when Transparency > 0
       Function RectFromPoints(const P:TFourPoints):TRect;
       begin
         with result do
         begin
           Left  :=Math.Min(P[3].X,Math.Min(P[2].X,Math.Min(P[0].X,P[1].X)));
           Top   :=Math.Min(P[3].Y,Math.Min(P[2].Y,Math.Min(P[0].Y,P[1].Y)));
           Right :=Math.Max(P[3].X,Math.Max(P[2].X,Math.Max(P[0].X,P[1].X)));
           Bottom:=Math.Max(P[3].Y,Math.Max(P[2].Y,Math.Max(P[0].Y,P[1].Y)));
         end;

         if ParentChart.View3D then
            result:=ACanvas.CalcRect3D(result,StartZ);
       end;

     begin
       if Transparency>0 then
          if ACanvas.SupportsAlpha then
             result:=ACanvas.BeginBlending(TeeZeroRect,Transparency)
          else
             result:=ACanvas.BeginBlending(RectFromPoints(P),Transparency)
       else
          result:=nil;
     end;

     function CalcBlend:TTeeBlend; overload;
     var tmpR : TRect;
     begin
       if Transparency>0 then
       begin
         if ACanvas.SupportsAlpha then
            result:=ACanvas.BeginBlending(TeeZeroRect,Transparency)
         else
         begin
           with ACanvas.Calculate3DPosition(X,Y,StartZ) do
           begin
             tmpR.Left:=X;
             tmpR.Top:=Y;
           end;

           with ACanvas.Calculate3DPosition(X,BottomPos,EndZ) do
           begin
             tmpR.Right:=X;
             tmpR.Bottom:=Y;
           end;

           result:=ACanvas.BeginBlending(tmpR,Transparency);
         end;
       end
       else
          result:=nil;
     end;

     function InternalUseLinePen:Boolean;
     begin
       result:=LinePen.Visible;

       if result then
          ParentChart.Canvas.AssignVisiblePen(LinePen);
     end;

     function PaintEdge:Boolean;
     begin
       result:=ParentChart.View3D and
            ( IsLastValue
              or
              ((ValueIndex<Count-1) and IsNull(Succ(ValueIndex))
                and (TreatNulls=tnDontPaint))
              or
              ((ValueIndex>0) and IsNull(Pred(ValueIndex))
                and (TreatNulls=tnDontPaint))
              );
     end;

     procedure DoPaintEdge(const OriginalColor:TColor);
     var tmpBlend  : TTeeBlend;
     begin
       if Dark3D then
          ACanvas.Brush.Color:=ApplyDark(ACanvas.Brush.Color,DarkColorQuantity);

       tmpBlend:=CalcBlend;

       if YMandatory then
          ACanvas.RectangleZ(X,Y,BottomPos,StartZ,EndZ)
       else
          ACanvas.RectangleY(X,Y,BottomPos,StartZ,EndZ);

       if Transparency>0 then
          ACanvas.EndBlending(tmpBlend);

       if Dark3D then
          ParentChart.SetBrushCanvas(BrushColor,FAreaBrush,OriginalColor);
     end;

     function OriginalAreaColor:TColor;
     begin
       result:=FAreaBrush.Color;

       if result=clTeeColor then
          result:=SeriesColor;

       if FAreaBrush.Image.Valid and (result=BrushColor) then
          result:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF}; // TV52010270
     end;

     function CalcSegmentPoints:TFourPoints;
     begin
       if YMandatory then
       begin
         result[0]:=TeePoint(OldX,OldBottomPos);
         result[3]:=TeePoint(X,BottomPos);
       end
       else
       begin
         result[0]:=TeePoint(OldBottomPos,OldY);
         result[3]:=TeePoint(BottomPos,Y);
       end;

       result[1]:=TeePoint(OldX,OldY);
       result[2]:=TeePoint(X,Y);
     end;

     function CalcFrontZ(const ARotation:Integer):Integer;
     begin
       if (ARotation>90) and (ARotation<270) then
          result:=EndZ
       else
          result:=StartZ;
     end;

     function CalcAreaStairRect:TRect;
     var tmpY      : Integer;
         tmpBottom : Integer;
     begin
       if FInvertedStairs then
       begin
         if DrawValuesForward then
         begin
           if YMandatory then tmpY:=Y
                         else tmpY:=X;
         end
         else
         begin
           if YMandatory then tmpY:=OldY
                         else tmpY:=OldX;
         end;

         tmpBottom:=BottomPos;
       end
       else
       begin
         if DrawValuesForward then
         begin
           if YMandatory then tmpY:=OldY
                         else tmpY:=OldX;
         end
         else
         begin
           if YMandatory then tmpY:=Y
                         else tmpY:=X;
         end;

         tmpBottom:=OldBottomPos;
       end;

       if YMandatory then
       begin
         {$IFNDEF FMX}
         if (TCanvasAccess(ParentChart.Canvas).CanvasStyle in [csGDIPlus,csOpenGL,csHTML5]) then
         {$ENDIF}
           Inc(OldX);

         if DrawValuesForward then
            result:=TeeRect(OldX,tmpBottom,X+1,tmpY)
         else
            result:=TeeRect(x+1,tmpBottom,OldX,tmpY)
       end
       else
       begin
         {$IFNDEF FMX}
         if (TCanvasAccess(ParentChart.Canvas).CanvasStyle in [csGDIPlus,csOpenGL,csHTML5]) then
         {$ENDIF}
           Dec(OldY);

         result:=TeeRect(tmpBottom,OldY+1,tmpY+1,Y);
       end;
     end;

     procedure DrawVerticalLine;
     begin
       if FInvertedStairs then
       begin
         if DrawValuesForward then
            ACanvas.LineWithZ(OldX,Y,X,Y,StartZ)
         else
            ACanvas.LineWithZ(OldX,OldY,X,OldY,StartZ);

         if ValueIndex>FirstValueIndex+1 then
            if DrawValuesForward then
               ACanvas.LineWithZ(OldX,OldY,OldX,Y,StartZ)
            else
               ACanvas.LineWithZ(X,OldY,X,Y,StartZ);
       end
       else
       begin
         if DrawValuesForward then
            ACanvas.LineWithZ(OldX,OldY,X,OldY,StartZ)
         else
            ACanvas.LineWithZ(OldX,Y,X,Y,StartZ);

         if ValueIndex<LastValueIndex then
            if DrawValuesForward then
               ACanvas.LineWithZ(X,OldY,X,Y,StartZ)
            else
               ACanvas.LineWithZ(OldX,OldY,OldX,Y,StartZ);

         if ValueIndex>(FirstDisplayedIndex+1) then
            ACanvas.LineWithZ(OldX,OldY,OldX,CalcYPos(ValueIndex-2),StartZ);
       end;
     end;

     procedure DrawHorizontalLine;
     begin
       if FInvertedStairs then
       begin
         if DrawValuesForward then
            ACanvas.LineWithZ(X,OldY,X,Y,StartZ)
         else
            ACanvas.LineWithZ(OldX,OldY,OldX,Y,StartZ);

         if ValueIndex>FirstValueIndex+1 then
            if DrawValuesForward then
               ACanvas.LineWithZ(OldX,OldY,X,OldY,StartZ)
            else
               ACanvas.LineWithZ(OldX,Y,X,Y,StartZ);
       end
       else
       begin
         if DrawValuesForward then
            ACanvas.LineWithZ(OldX,OldY,OldX,Y,StartZ)
         else
            ACanvas.LineWithZ(X,OldY,X,Y,StartZ);

         if ValueIndex<LastValueIndex then
            if DrawValuesForward then
               ACanvas.LineWithZ(OldX,Y,X,Y,StartZ)
            else
               ACanvas.LineWithZ(OldX,OldY,X,OldY,StartZ);

         if ValueIndex>(FirstDisplayedIndex+1) then
            ACanvas.LineWithZ(OldX,OldY,CalcXPos(ValueIndex-2),OldY,StartZ);
       end;
     end;

     procedure DrawStairSegment;
     var tmpR     : TRect;
         tmpBlend : TTeeBlend;
     begin
       tmpR:=CalcAreaStairRect;

       tmpBlend:=CalcBlend(tmpR);

       with ACanvas do
       begin
         if ParentChart.View3D then
         begin
           RectangleWithZ(tmpR,StartZ);

           if SupportsFullRotation then
              RectangleWithZ(tmpR,EndZ);
         end
         else
           Rectangle(tmpR);

         if Transparency>0 then
            EndBlending(tmpBlend);
       end;

       if LinePen.Visible then // TV52012701
       begin
         ACanvas.AssignVisiblePen(LinePen);

         if YMandatory then
            DrawVerticalLine
         else
            DrawHorizontalLine;
       end;
     end;

     procedure DrawNormalSegment;
     var tmpP     : TFourPoints;
         tmpBlend : TTeeBlend;
     begin
       tmpP:=CalcSegmentPoints;

       tmpBlend:=CalcBlend(tmpP);

       if ParentChart.View3D then
          ACanvas.PlaneWithZ(tmpP,CalcFrontZ(ParentChart.View3DOptions.Rotation))
       else
          ACanvas.Polygon(tmpP);

       if ACanvas.SupportsFullRotation then
          ACanvas.PlaneWithZ(tmpP,EndZ); // Back-face plane

       if PrepareHoverPen(ValueIndex-1) or InternalUseLinePen then
          ACanvas.LineWithZ(OldX,OldY,X,Y,StartZ);

       if Transparency>0 then
          ACanvas.EndBlending(tmpBlend);
     end;

   var tmpColor2 : TColor;
   begin
     tmpColor2:=OriginalAreaColor;

     ParentChart.SetBrushCanvas(BrushColor,FAreaBrush,tmpColor2);

     // Draw side at last Area edge, or inside middle Null area points.
     if PaintEdge then
        DoPaintEdge(tmpColor2);

     if FStairs then
        DrawStairSegment
     else
        DrawNormalSegment; // not in "stairs" mode...
   end;

   procedure DrawLine2D;

     function CreateBlend:TTeeBlend;
     var tmpPenWidth : Integer;
     begin
       if Transparency>0 then
       begin
         if ParentChart.Canvas.SupportsAlpha then
            result:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transparency)
         else
         begin
           tmpPenWidth:={$IFDEF FMX}Round{$ENDIF}(LinePen.Width);

           result:=ParentChart.Canvas.BeginBlending(
                                   TeeRect(Min(OldX,X)-tmpPenWidth,
                                           Min(OldY,Y)-tmpPenWidth,
                                           Max(OldX,X)+tmpPenWidth,
                                           Max(OldY,Y)+tmpPenWidth),
                                   Transparency);
         end;
       end
       else
         result:=nil;
     end;

   var tmpBlend : TTeeBlend;
   begin
     if not PrepareHoverPen(ValueIndex-1) then
        if (ColorEachPoint and not Pointer.Visible) or ColorEachLine then
           LinePrepareCanvas(ParentChart.Canvas,tmpColor)
        else
           LinePrepareCanvas(ParentChart.Canvas,SeriesColor);

     tmpBlend:=CreateBlend;

     if FStairs then
     begin
       if FInvertedStairs then
          ParentChart.Canvas.DoVertLine(OldX,OldY,Y)
       else
          ParentChart.Canvas.DoHorizLine(OldX,X,OldY);

       ParentChart.Canvas.LineTo(X,Y);
     end
     else
        ParentChart.Canvas.Line(OldX,OldY,X,Y);

     if Transparency>0 then
        ParentChart.Canvas.EndBlending(tmpBlend);
   end;

   procedure DoAddPathPoint(const X,Y:Double);
   var tmp : TColor;
   begin
     tmp:=InternalColor(ValueIndex);

     if tmp=clTeeColor then
        if ColorEachPoint then
           tmp:=ValueColor[ValueIndex]
        else
           tmp:=clDefault;

     with TVisualAccess(Visuals.Template) do
          AddPathPoint(X,ParentChart.Height-Y,tmp);
   end;

   procedure TryDrawPointer;
   begin
     if FPointer.Visible and (tmpColor<>clNone) then {<-- if not null }
        DrawPointer(X,Y,tmpColor,ValueIndex);
   end;

   function BlendFromPolygon(P:TFourPoints):TTeeBlend;
   begin
     Offset(P[0],-Pen.Width);
     Offset(P[1],-Pen.Width);

     Offset(P[2],Pen.Width);
     Offset(P[3],Pen.Width);

     result:=ParentChart.Canvas.BeginBlending(RectFromPolygon(P,4),Transparency)
   end;

   function BlendFromPoint(const P:TPoint):TTeeBlend;
   begin
     result:=ParentChart.Canvas.BeginBlending(
                           ParentChart.Canvas.RectFromRectZ(
                               TeeRect(P.X,P.Y,
                                       P.Y+FLineHeight+Pen.Width,
                                       P.X+FLineHeight+Pen.Width),
                               {$IFDEF FMX}Round{$ENDIF}(EndZ+Pen.Width)),
                           Transparency);
   end;

   Procedure DrawPoint(DrawOldPointer:Boolean);
   var
     tmpPoint : TPoint;
     tmpOldP  : TPoint;

     function BlendFromBounds:TTeeBlend;
     var tmp : Integer;
         tmpP : TFourPoints;
         tmpCanvas : TCanvas3D;
     begin
       tmp:={$IFDEF FMX}Round{$ENDIF}(LinePen.Width);

       tmpCanvas:=ParentChart.Canvas;

       tmpP[0]:=tmpCanvas.Calculate3DPosition(tmpPoint,StartZ-tmp);
       tmpP[1]:=tmpCanvas.Calculate3DPosition(tmpOldP,StartZ-tmp);
       tmpP[2]:=tmpCanvas.Calculate3DPosition(tmpOldP,EndZ+tmp);
       tmpP[3]:=tmpCanvas.Calculate3DPosition(tmpPoint,EndZ+tmp);

       result:=tmpCanvas.BeginBlending(PolygonBounds(tmpP),Transparency);
     end;

     procedure DrawLineHeight;
     var P4       : TFourPoints;
         tmpBlend : TTeeBlend;
     begin
       P4[0]:=tmpPoint;
       P4[1]:=tmpOldP;

       P4[2].X:=tmpOldP.X;
       P4[2].Y:=tmpOldP.Y+LineHeight;

       P4[3].X:=tmpPoint.X;
       P4[3].Y:=tmpPoint.Y+LineHeight;

       if Transparency>0 then
       begin
         if ParentChart.Canvas.SupportsAlpha then
            tmpBlend:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transparency)
         else
            tmpBlend:=BlendFromPolygon(P4);
       end
       else
          tmpBlend:=nil;

       ParentChart.Canvas.PlaneFour3D(P4,StartZ,StartZ);

       if IsLastValue then
       begin
          if Transparency>0 then
          begin
            if not ParentChart.Canvas.SupportsAlpha then
            begin
              ParentChart.Canvas.EndBlending(tmpBlend);
              tmpBlend:=BlendFromPoint(tmpPoint);
            end;
          end
          else
            tmpBlend:=nil;

          ParentChart.Canvas.RectangleZ({$IFDEF FMX}Round{$ENDIF}(tmpPoint.X),
                   {$IFDEF FMX}Round{$ENDIF}(tmpPoint.Y),
                   {$IFDEF FMX}Round{$ENDIF}(tmpPoint.Y)+FLineHeight,StartZ,EndZ);
       end;

       if Transparency>0 then
          ParentChart.Canvas.EndBlending(tmpBlend);
     end;

   var
     OldDarkColor : TColor;

     procedure DrawStairs;
     begin
       with ParentChart.Canvas do
       begin
         if InvertedStairs then
         begin
           if FDark3D then
              Brush.Color:=ApplyDark(Brush.Color,DarkColorQuantity);

           if YMandatory then
           begin
             if DrawValuesForward then
               RectangleZ( {$IFDEF FMX}Round{$ENDIF}(tmpOldP.X),
                           {$IFDEF FMX}Round{$ENDIF}(tmpOldP.Y), Y,StartZ,EndZ)
             else
               RectangleZ( x,
                 {$IFDEF FMX}Round{$ENDIF}(tmpOldP.Y), Y,StartZ,EndZ)
           end
           else
              RectangleY( {$IFDEF FMX}Round{$ENDIF}(tmpPoint.X),
                          {$IFDEF FMX}Round{$ENDIF}(tmpPoint.Y),
                          OldX,StartZ,EndZ);

           if FDark3D then
              Brush.Color:=OldDarkColor;

           if YMandatory then
           begin
             if DrawValuesForward then
               RectangleY( {$IFDEF FMX}Round{$ENDIF}(tmpPoint.X),
                           {$IFDEF FMX}Round{$ENDIF}(tmpPoint.Y),
                           OldX,StartZ,EndZ)
             else
               RectangleY( OldX,OldY,x,StartZ,EndZ)
           end
           else
              RectangleZ( X, {$IFDEF FMX}Round{$ENDIF}(tmpOldP.Y), Y, StartZ, EndZ);
         end
         else
         begin
           if YMandatory then
           begin
             if DrawValuesForward then
               RectangleY( {$IFDEF FMX}Round{$ENDIF}(tmpOldP.X),
                           {$IFDEF FMX}Round{$ENDIF}(tmpOldP.Y),
                           X, StartZ, EndZ)
             else
               RectangleY( {$IFDEF FMX}Round{$ENDIF}(tmpOldP.X), y, x, StartZ, EndZ)
           end
           else
           begin
             if DrawValuesForward then
                RectangleZ( OldX, {$IFDEF FMX}Round{$ENDIF}(tmpOldP.Y), Y, StartZ, EndZ)
             else
                RectangleZ( x, {$IFDEF FMX}Round{$ENDIF}(tmpOldP.Y), Y, StartZ, EndZ)
           end;

           if FDark3D then
              Brush.Color:=ApplyDark(Brush.Color,DarkColorQuantity);

           if YMandatory then
           begin
             if DrawValuesForward then
                RectangleZ( {$IFDEF FMX}Round{$ENDIF}(tmpPoint.X),
                            {$IFDEF FMX}Round{$ENDIF}(tmpPoint.Y),
                           OldY,StartZ,EndZ)
             else
                RectangleZ( OldX, OldY, y, StartZ,EndZ)
           end
           else
           begin
             if DrawValuesForward then
                RectangleY( OldX, y, x, StartZ,EndZ)
             else
                RectangleY( OldX, OldY, x, StartZ,EndZ)
           end;

           if FDark3D then
              Brush.Color:=OldDarkColor;
         end;
       end;
     end;

     procedure AddStairsTemplate;
     begin
       if InvertedStairs then
          if YMandatory then
          begin
            DoAddPathPoint(tmpOldP.X,Y);
            DoAddPathPoint(X,tmpPoint.Y);
          end
          else
          begin
            DoAddPathPoint(OldX,tmpPoint.Y);
            DoAddPathPoint(X,Y);
          end
       else
          if YMandatory then
          begin
            DoAddPathPoint(X,tmpOldP.Y);
            DoAddPathPoint(X,Y);
          end
          else
          begin
            DoAddPathPoint(OldX,Y);
            DoAddPathPoint(X,tmpPoint.Y);
          end;
     end;

     procedure AddLineTemplate;
     begin
       with tmpPoint do
            DoAddPathPoint(X,Y);
     end;

     procedure DrawLinePlane;
     var tmpDark3D   : Boolean;
         tmpDifX     : TCoordinate;
         tmpBlend    : TTeeBlend;
     begin
       if (LineHeight<>0) and (not FDrawArea) then
          DrawLineHeight;

       with ParentChart.Canvas do
       begin
         tmpDark3D:=FDark3D and (not SupportsFullRotation);

         if tmpDark3D then
         begin
           tmpDifX:=tmpPoint.X-tmpOldP.X;

           if (tmpDifX<>0) and
              (tmpDark3DRatio<>0) and
              ((tmpOldP.Y-tmpPoint.Y)/tmpDifX > tmpDark3DRatio) then
                  Brush.Color:=ApplyDark(Brush.Color,DarkColorQuantity);
         end;

         if Monochrome then
            Brush.Color:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};

         if Transparency>0 then
         begin
           if SupportsAlpha then
              tmpBlend:=BeginBlending(TeeZeroRect,Transparency) // GDI+ etc
           else
              tmpBlend:=BlendFromBounds;
         end
         else
           tmpBlend:=nil;

         Self.ParentChart.SetBrushCanvas(Brush.Color,Self.Brush,Self.Brush.BackColor);
         Plane3D(tmpPoint,tmpOldP,StartZ,EndZ);

         if Transparency>0 then
            EndBlending(tmpBlend);

         if tmpDark3D then
            Brush.Color:=OldDarkColor;
       end;
     end;

     procedure TryFixOverflowCoordinates;

       // Avoids XY pixel coordinates overflow maximum GDI limits
       // (+- 32767 in Windows XP)
       procedure CheckMaxPixelXY(var APos,AOther:Integer);
       const
         MaxAttempts = 5;

       var tmpMax, tmpOMax,
           tmpAttempts: Integer;
           tmpIndex : Integer;
           tmpContinue : Boolean;
           tmpAxis : TChartAxis;
       begin
         if stairs then
         Begin
           if (APos>TeeMaxPixelPosLine) or (APos<-TeeMaxPixelPosLine)
             or (AOther>TeeMaxPixelPosLine) or (AOther<-TeeMaxPixelPosLine) then
           begin
             if APos>TeeMaxPixelPosLine then tmpMax:=TeeMaxPixelPosLine
             else if APos<-TeeMaxPixelPosLine then tmpMax:=-TeeMaxPixelPosLine
             else tmpMax:=APos;

             if AOther>TeeMaxPixelPosLine then tmpOMax:=TeeMaxPixelPosLine
             else if AOther<-TeeMaxPixelPosLine then tmpOMax:=-TeeMaxPixelPosLine
             else tmpOMax:=AOther;

             APos:=tmpMax;
             AOther:=tmpOMax;
           end;
         end
         else
         Begin
           if (APos>TeeMaxPixelPosLine) or (APos<-TeeMaxPixelPosLine) then
           begin
             if APos>TeeMaxPixelPosLine then tmpMax:=TeeMaxPixelPosLine
                                        else tmpMax:=-TeeMaxPixelPosLine;

             if YMandatory then
                tmpAxis:=GetHorizAxis
             else
                tmpAxis:=GetVertAxis;

             if tmpAxis.Inverted then //TV52016351
                tmpIndex:=ValueIndex
             else
                tmpIndex:=Pred(ValueIndex);

             tmpAttempts:=0;

             repeat
               Inc(tmpAttempts);

               AOther:=CalcPosLeftRight(tmpAxis.CalcPosPoint(tmpMax),ValueIndex,tmpIndex);

               tmpContinue:=((AOther>TeeMaxPixelPosLine) or (AOther<-TeeMaxPixelPosLine)) and
                            (tmpAttempts<MaxAttempts);

               if tmpContinue then
                  tmpMax:=tmpMax div 3;

             until not tmpContinue;

             if AOther>TeeMaxPixelPosLine then
                AOther:=TeeMaxPixelPosLine
             else
             if AOther<-TeeMaxPixelPosLine then
                AOther:=-TeeMaxPixelPosLine;

             APos:=tmpMax;
           end;
         end;
       end;

       function OldInsideChart:Boolean;
       begin
         if stairs then
         begin
           if ((OldX<=0) or (OldX>=ParentChart.Width))
               or ((OldY<=0) or (OldY>=ParentChart.Height)) then
                 result := false
           else
             result:=true;
         end
         else
           if YMandatory then
              result:=(OldX>=0) and (OldX<=ParentChart.Width)
           else
              result:=(OldY>=0) and (OldY<=ParentChart.Height);
       end;

     begin
       if not OldInsideChart then
          if YMandatory then
             CheckMaxPixelXY(OldX,OldY)
          else
             CheckMaxPixelXY(OldY,OldX);

       if YMandatory then
          CheckMaxPixelXY(X,Y)
       else
          CheckMaxPixelXY(Y,X);
     end;

     function LineInsideChart:Boolean;
     var R : TRect;
     begin
       if OldX<X then R.Left:=OldX else R.Left:=X;
       if OldX>X then R.Right:=OldX else R.Right:=X;

       if R.Left=R.Right then R.Right:=R.Right+1;

       if OldY<Y then R.Top:=OldY else R.Top:=Y;
       if OldY>Y then R.Bottom:=OldY else R.Bottom:=Y;

       if R.Top=R.Bottom then R.Bottom:=R.Bottom+1;

       result:=TeeRectIntersects(R,IClientRect);
     end;

     procedure DoDrawPointer(Behind:Boolean);
     begin
       if FPointer.Visible and DrawOldPointer and (Behind=PointerBehind) then
       begin
         tmpColor:=ValueColor[ValueIndex];

         if OldColor=clNone then
         begin
           if TreatNulls<>tnDontPaint then
              DrawPointer(OldX,OldY,tmpColor,PreviousValueIndex);
         end
         else
           DrawPointer(OldX,OldY,OldColor,PreviousValueIndex);

         if IsLastValue then
            TryDrawPointer;
       end;
     end;

   begin
     DoDrawPointer(True);

     if ((X<>OldX) or (Y<>OldY)) and
        ((TreatNulls=tnIgnore) or (tmpColor<>clNone)) then
     with ParentChart,Canvas do
     begin

       // 3D
       if View3D then
       begin
         if FDrawArea or FDrawLine then
         begin

           if FColorEachLine or FDrawArea then
              if Self.Brush.Color=clTeeColor then
                 OldDarkColor:=GetAreaBrushColor(tmpColor)
              else
                 OldDarkColor:=GetAreaBrushColor(self.Brush.Color)
           else
              OldDarkColor:=SeriesColor;

           if Self.Brush.Image.Valid then
              SetBrushCanvas(OldDarkColor,Self.Brush,{$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF}) // TV52012447
           else
           begin
             if Brush.Color<>OldDarkColor then
                Brush.Color:=OldDarkColor;

             if Brush.Style<>LineBrush then
                Brush.Style:=LineBrush;
           end;

         //CDI TV52012170
         // DB: This fix breaks LinePen.Color
         // if LinePen.Color<>OldDarkColor then
         //    LinePen.Color:=OldDarkColor;

           if not PrepareHoverPen(ValueIndex-1) then
              AssignVisiblePen(LinePen);

           tmpPoint.X:=X;
           tmpPoint.Y:=Y;
           tmpOldP.X:=OldX;
           tmpOldP.Y:=OldY;

           if FStairs then
              if HasTemplate then
                 AddStairsTemplate
              else
                 DrawStairs
           else
              if HasTemplate then
                 AddLineTemplate
              else
                 DrawLinePlane;
         end;
       end;

       // Area
       if FDrawArea then
       begin
         if AreaChartBrush.BackColor=clTeeColor then
            Brush.Color:=GetAreaBrushColor(tmpColor)
         else
            Brush.Color:=GetAreaBrushColor(AreaChartBrush.BackColor);

         if not PrepareHoverPen(ValueIndex-1) then
           if (FAreaLinesPen.Color=clTeeColor) or (not FAreaLinesPen.Visible) then
              AssignVisiblePenColor(FAreaLinesPen,tmpColor)
           else
              AssignVisiblePen(FAreaLinesPen);

         TryFixOverflowCoordinates;
         InternalDrawArea(Brush.Color,Canvas);
       end
       else
       if (not View3D) and FDrawLine then
       begin
          if ColorEachLine then
             tmpColor:=ValueColor[ValueIndex]
          else
          if LinePen.Color<>SeriesColor then
             tmpColor:=LinePen.Color;

          if LineInsideChart then
          begin
            TryFixOverflowCoordinates;
            DrawLine2D;
          end;
       end;
     end;

     DoDrawPointer(False);
   end;

   procedure DrawPreviousPoint;
   var tmp : Integer;
   begin
     tmp:=Pred(ValueIndex);

     if FDrawArea then
     begin
       OldX:=CalcXPos(tmp);
       OldY:=CalcYPos(tmp);
       OldBottomPos:=GetOriginPos(tmp);
     end
     else
     begin
       if YMandatory then
       begin
         if GetHorizAxis.Inverted then
            OldX:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Right)
         else
            OldX:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Left);

         if FStairs Then
            OldY:=CalcYPos(tmp)
         else
            OldY:=CalcPosLeftRight(XScreenToValue(OldX),ValueIndex,tmp)
       end
       else
       begin
         if GetVertAxis.Inverted then
            OldY:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Bottom)
         else
            OldY:={$IFDEF FMX}Round{$ENDIF}(ParentChart.ChartRect.Top);

         if FStairs Then
            OldX:=CalcXPos(tmp)
         else
            OldX:=CalcPosLeftRight(YScreenToValue(OldY),ValueIndex,tmp)
       end;
     end;

     if not IsNull(tmp) then
        DrawPoint(False);
   end;

var tmpFirst : Integer;
    tmpLast  : Integer;
    i        : Integer;
begin
  // TV52014016
  if FDrawArea and not DrawValuesForward then
     tmpColor:=ValueColor[ValueIndex+1]
  else
     tmpColor:=ValueColor[ValueIndex];

  X:=CalcXPos(ValueIndex);
  Y:=CalcYPos(ValueIndex);

  tmpFirst:=FirstDisplayedIndex;

  if (TreatNulls=tnSkip) and IsNull(ValueIndex) then
    if ValueIndex<>tmpFirst then
    begin
      // skip
      X:=OldX;
      Y:=OldY;
    end
    else
      for i:=ValueIndex+1 to Count-1 do // Skip until next non-null
        if not IsNull(i) then
        begin
          X:=CalcXPos(i);
          Y:=CalcYPos(i);
          break;
        end;

  if OldColor=clNone then { if null }
  if TreatNulls=tnDontPaint then
  begin
    OldX:=X;
    OldY:=Y;
  end;

  BottomPos:=GetOriginPos(ValueIndex);

  tmpLast:=LastDisplayedIndex;
  IsLastValue:=ValueIndex=tmpLast;

  //TV52015190
  if DrawValuesForward then
  begin
    PreviousValueIndex:=Pred(ValueIndex);

    if PreviousValueIndex<FFirstVisibleIndex then
       PreviousValueIndex:=FFirstVisibleIndex;
  end
  else
  begin
    PreviousValueIndex:=Succ(ValueIndex);

    if PreviousValueIndex>FLastVisibleIndex then
       PreviousValueIndex:=FLastVisibleIndex;
  end;

  with ParentChart.Canvas do
  if tmpColor<>Brush.Color then
     Brush.Color:=tmpColor;

  if ValueIndex=tmpFirst then // <-- First point
  begin
    if not HasTemplate then
       if FDark3D then
       with ParentChart do
          if SeriesWidth3D<>0 then
             tmpDark3DRatio:=Abs(SeriesHeight3D/SeriesWidth3D)
          else
             tmpDark3DRatio:=1;

    // Previous point outside left/right
    if (tmpFirst=FirstValueIndex) and (ValueIndex>0) then
       DrawPreviousPoint
    else
    if HasTemplate then
       DoAddPathPoint(X,Y);

    // Draw last pointer (only if point is not "null")
    if IsLastValue or ParentChart.Canvas.SupportsFullRotation then
       TryDrawPointer;

    if (not HasTemplate) and FDrawArea and ParentChart.View3D then
        with ParentChart.Canvas do
        if SupportsFullRotation then
           RectangleZ(X,Y,BottomPos,StartZ,EndZ);
  end
  else
    if ValueIndex=tmpLast then //TV52014257
    begin
      DrawPoint(not ParentChart.Canvas.SupportsFullRotation);

      if (tmpFirst=LastValueIndex) and (ValueIndex>0) then // <- previous point outside left
         DrawPreviousPoint;

      if ParentChart.Canvas.SupportsFullRotation then
         TryDrawPointer;
    end
    else
    if ParentChart.Canvas.SupportsFullRotation then
    begin
      DrawPoint(False);
      TryDrawPointer;
    end
    else
      DrawPoint(True);

  OldX:=X;
  OldY:=Y;
  OldBottomPos:=BottomPos;
  OldColor:=tmpColor;
end;

procedure TCustomSeries.DrawHiddenValue(ValueIndex:Integer;
                                        const StartValue,EndValue:TAxisValue;
                                        GapSize:Integer; Axis:TChartAxis);
var tX : Integer;
    tY : Integer;
    tmpGapSize : integer;
begin
  tX:=CalcXPos(ValueIndex);
  tY:=CalcYPos(ValueIndex);

  tmpGapSize := (GapSize div 2);

  if (not Axis.Horizontal) and (not GetVertAxis.Inverted) then
    tmpGapSize := -tmpGapSize
  else if (Axis.Horizontal) and (GetHorizAxis.Inverted) then
    tmpGapSize := -tmpGapSize;

  if Axis.Horizontal then
     tX:=CalcXPosValue(StartValue) + tmpGapSize
  else
     tY:=CalcYPosValue(StartValue) + tmpGapSize;

  if (Axis.Horizontal and ((XValue[pred(ValueIndex)]<=StartValue) and (XValue[pred(ValueIndex)]>=EndValue)))
     or ((not Axis.Horizontal) and (YValue[pred(ValueIndex)]>=StartValue) and (YValue[pred(ValueIndex)]<=EndValue)) then
  begin
     ParentChart.Canvas.Pen.Style := psClear;
  end
  else
  begin
    ParentChart.Canvas.AssignVisiblePen(Pen);
  end;

  ParentChart.Canvas.Line(OldX,OldY,tX,tY);

  if FPointer.Visible then
     if OldColor=clNone then
     begin
       if TreatNulls<>tnDontPaint then
          DrawPointer(OldX,OldY,Brush.Color,Pred(ValueIndex));
     end
     else
       DrawPointer(OldX,OldY,OldColor,Pred(ValueIndex));

  OldX := tX;
  OldY := tY;
end;

procedure TCustomSeries.InitDrawValue(ValueIndex:Integer);
begin
  if ValueIndex>0 then
  begin
    OldX:=CalcXPos(ValueIndex-1);
    OldY:=CalcYPos(ValueIndex-1);
    OldBottomPos:=GetOriginPos(ValueIndex-1);
    OldColor:=ValueColor[ValueIndex-1];
  end;
end;

Procedure TCustomSeries.DrawVisual(const R:TRect;
                                   const AItem:TSeriesPointer;
                                   const AVisual:TVisualBlock;
                                   const AColor:TColor);
begin
  TVisualAccess(AVisual).SetBounds(R,TPointerAccess(AItem).GetStartZ,
                             TPointerAccess(AItem).GetEndZ,ParentChart.Height);

  TVisualAccess(AVisual).SetFormatting(Pen,Brush,False,AColor,Transparency);
end;

type
  TCanvas3DAccess=class(TCanvas3D);

Procedure TCustomSeries.DrawPointer(AX,AY:Integer; AColor:TColor; ValueIndex:Integer);
var tmpStyle : TSeriesPointerStyle;
    tmp      : TVisualBlock;
    tmpItem  : TSeriesPointer;
begin
  tmpItem:=Pointer.InternalGet(ValueIndex);

  if not tmpItem.Visible then
     Exit;

  if (tmpItem.Brush.Color<>clTeeColor) and (tmpItem.Brush.Style<>bsClear) then
     AColor:=tmpItem.Brush.Color;

  // Set canvas properties to Pointer (Brush, Color, Pen)
  tmpItem.PrepareCanvas(ParentChart.Canvas,AColor);

  PreparePointerHoverPen(ValueIndex);

  // Call the OnGetPointerStyle event if assigned.
  tmpStyle:=DoGetPointerStyle(ValueIndex);

  // Repeat again setting Canvas parameters, thus allowing the developer
  // to use the OnGetPointerStyle event to modify other Pointer properties
  // like Color, Pen, etc.
  if Assigned(FOnGetPointerStyle) then
     tmpItem.PrepareCanvas(ParentChart.Canvas,AColor);

  TCanvas3DAccess(ParentChart.Canvas).BeginEntity('SeriesPointer',nil,TPointerAccess(tmpItem).Transform);

  if tmpStyle=psVisual then
     tmp:=Visuals[ValueIndex]
  else
     tmp:=nil;

  if Assigned(tmp) then
     DrawVisual(RectFromCenter(AX,AY,tmpItem.HorizSize,tmpItem.VertSize),tmpItem,tmp,AColor)
  else
     tmpItem.Draw(AX,AY,AColor,tmpStyle);

  TCanvas3DAccess(ParentChart.Canvas).EndEntity;
end;

class Function TCustomSeries.GetEditorClass:String;
Begin
  result:='TCustomSeriesEditor'; { <-- dont translate ! }
end;

Procedure TCustomSeries.InternalCalcMargin(SameSide,Horizontal:Boolean; var A,B:Integer);

  procedure CalcPen(const APen:TTeePen);
  var tmp : Integer;
  begin
    if FStairs then
    begin
      tmp:={$IFDEF FMX}Round{$ENDIF}(APen.Width);
      A:=Math.Max(A,tmp);
      B:=Math.Max(B,tmp);
    end
    else
    if APen.Width>1 then
    begin
      tmp:=Round(APen.Width*0.5)+1;
      A:=Math.Max(A,tmp);
      B:=Math.Max(B,tmp);
    end;
  end;

var tmp : Integer;
    Dummy : Integer;
begin
  if Horizontal then
  begin
    if (Count=0) or (Pointer.Count=0) then
       TPointerAccess(FPointer).CalcHorizMargins(A,B)
    else
    begin
      TPointerAccess(Pointer.InternalGet(0)).CalcHorizMargins(A,Dummy);
      TPointerAccess(Pointer.InternalGet(Count-1)).CalcHorizMargins(Dummy,B);
    end;
  end
  else
     TPointerAccess(FPointer).CalcVerticalMargins(A,B);

  if InflateMargins then
  begin
    if FDrawLine then
    begin
      CalcPen(LinePen);

      if Assigned(FOutLine) and FOutLine.Visible then
      begin
        tmp:={$IFDEF FMX}Round{$ENDIF}(FOutLine.Width);
        A:=Math.Max(A,tmp);
        B:=Math.Max(B,tmp);
      end;
    end;

    if FDrawArea and Pen.Visible then
       CalcPen(Pen);
  end;

  if Marks.Visible and SameSide then
  begin
    tmp:=CalcMarkLength;
    if YMandatory then A:=Math.Max(B,tmp)
                  else B:=Math.Max(A,tmp);
  end;
end;

function TCustomSeries.IsPointInChartRect(ValueIndex: Integer): Boolean;
var tmpX,
    tmpY : Integer;
    tmpR : TRect;
begin
  tmpX:=CalcXPos(ValueIndex);
  tmpY:=CalcYPos(ValueIndex);

  tmpR:=RectFromCenter(tmpX,tmpY,Pointer.HorizSize,Pointer.VertSize);

  result:=TeeRectIntersects(ParentChart.ChartRect,tmpR);
end;

Function TCustomSeries.CalcMarkLength:Integer;
begin
  result:=Marks.Callout.Length+Marks.Callout.Distance;
end;

procedure TCustomSeries.CalcFirstLastVisibleIndex;
begin
  inherited;

  while (FLastVisibleIndex < Count - 1) and IsPointInChartRect(FLastVisibleIndex + 1) do
         Inc(FLastVisibleIndex);

  while (FLastVisibleIndex > -1) and (FLastVisibleIndex < Count - 1) and
        (NotMandatoryValueList.Items[FLastVisibleIndex] = NotMandatoryValueList.Items[FLastVisibleIndex+1]) do
         Inc(FLastVisibleIndex);

  while (FFirstVisibleIndex > 0) and IsPointInChartRect(FFirstVisibleIndex - 1) do
         Dec(FFirstVisibleIndex);

  while (FFirstVisibleIndex > 0) and
        (NotMandatoryValueList.Items[FFirstVisibleIndex] = NotMandatoryValueList.Items[FFirstVisibleIndex-1]) do
         Dec(FFirstVisibleIndex);
end;

Procedure TCustomSeries.CalcHorizMargins(var LeftMargin,RightMargin:Integer);
begin
  inherited;
  InternalCalcMargin(not YMandatory,True,LeftMargin,RightMargin);
end;

Procedure TCustomSeries.CalcVerticalMargins(var TopMargin,BottomMargin:Integer);
begin
  inherited;
  InternalCalcMargin(YMandatory,False,TopMargin,BottomMargin);

  if (FLineHeight>0) and (not FDrawArea) and InflateMargins and ParentChart.View3D then
     if FLineHeight>BottomMargin then
        BottomMargin:=FLineHeight;
end;

Procedure TCustomSeries.CalcZOrder;
Begin
  if FStacked=cssNone then inherited
                      else IZOrder:=ParentChart.MaxZOrder;
End;

function TCustomSeries.SameClassOrigin(ASeries:TChartSeries):Boolean;
begin
  result:=ASeries.SameClass(Self);
end;

Function TCustomSeries.PointOrigin(ValueIndex:Integer; SumAll:Boolean):Double;
var t         : Integer;
    tmpSeries : TChartSeries;
    tmp       : Double;
Begin
  result:=0;

  if Assigned(ParentChart) then
  with ParentChart do
  begin
    for t:=0 to SeriesCount-1 do
    Begin
      tmpSeries:=Series[t];

      if (not SumAll) and (tmpSeries=Self) then Break
      else
      With tmpSeries do
      if Active and SameClassOrigin(tmpSeries) and (Count>ValueIndex) then
      begin
        tmp:=GetOriginValue(ValueIndex);

        //TV52012757
        if SumAll and (tmp<0) then
           result:=result+(-tmp)
        else
           result:=result+tmp;
      end;
    end;
  end;
end;

Function TCustomSeries.CalcStackedPos(ValueIndex:Integer; Value:Double):Integer;
var tmp : Double;
begin
  Value:=Value+PointOrigin(ValueIndex,False);

  if FStacked=cssStack then
     result:=Math.Min(MandatoryAxis.IEndPos,CalcPosValue(Value))
  else
  begin
    tmp:=PointOrigin(ValueIndex,True);

    if tmp<>0 then result:=CalcPosValue(Value*100.0/tmp)
              else result:=MandatoryAxis.IEndPos;
  end;
end;

Function TCustomSeries.GetOriginPos(ValueIndex:Integer):Integer;
Begin
  if (FStacked=cssNone) or (FStacked=cssOverlap) then
     if YMandatory then
     begin
       with GetVertAxis do
       if Inverted then result:=IStartPos else result:=IEndPos;
     end
     else
     begin
       with GetHorizAxis do
       if Inverted then result:=IEndPos else result:=IStartPos;
     end
  else
     result:=CalcStackedPos(ValueIndex,0);
end;

Function TCustomSeries.MaxXValue:TChartValue;
var t : Integer;
Begin
  if YMandatory then
     result:=inherited MaxXValue
  else
  begin
    if FStacked=cssStack100 then
       result:=100
    else
    begin
      result:=CalcMinMaxValue(False);

      if FStacked=cssStack then
         for t:=0 to Count-1 do
             result:=Math.Max(result,PointOrigin(t,False)+XValues.Value[t]);
    end;
  end;
end;

Function TCustomSeries.MinXValue:TChartValue;
Begin
  if not YMandatory then
     if FStacked=cssStack100 then
        result:=0
     else
        result:=CalcMinMaxValue(True)
  else
     result:=inherited MinXValue;
end;

Function TCustomSeries.MaxYValue:TChartValue;
var t : Integer;
Begin
  if not YMandatory then
     result:=inherited MaxYValue
  else
  begin
    if FStacked=cssStack100 then
       result:=100
    else
    begin
      result:=CalcMinMaxValue(False);

      if FStacked=cssStack then
      for t:=0 to Count-1 do
          result:=Math.Max(result,PointOrigin(t,False)+YValues.Value[t]);
    end;
  end;
end;

function TCustomSeries.CalcMinMaxValue(IsMin:Boolean):TChartValue;
var t : Integer;
    tmpFirst : Boolean;
    tmpValue : TChartValue;
begin
  result:=0;
  tmpFirst:=True;

  for t:=0 to Count-1 do
  begin
    if IsPainted(t) then
    begin
      tmpValue:=MandatoryValueList.Value[t];

      if tmpFirst then
      begin
        result:=tmpValue;
        tmpFirst:=False;
      end
      else
      if IsMin then
         result:=Math.Min(result,tmpValue)
      else
         result:=Math.Max(result,tmpValue);
    end;
  end;
end;

Function TCustomSeries.MinYValue:TChartValue;
var t : Integer;
    tmp: Double;
Begin
//  if YMandatory and (FStacked=cssStack100) then  //TV52012757. Remove this, if there are negative values it could not be 0
//     result:=0
//  else
  begin
    if YMandatory then
       result:=CalcMinMaxValue(True)
    else
       result:=inherited MinYValue;

    if FStacked=cssStack then
    begin
      for t:=0 to Count-1 do
          result:=Math.Min(result,PointOrigin(t,False)+YValues.Value[t]);
    end
    else
    if FStacked=cssStack100 then
    begin
      result:=0;
      for t:=0 to Count-1 do
      begin
        tmp:=PointOrigin(t,True);
        if tmp<>0 then
           result:=Math.Min(result,(PointOrigin(t,False)+YValues.Value[t])*100/tmp)
        else
           result:=Math.Min(result, MandatoryAxis.IEndPos);
      end
    end;
  end;
end;

Function TCustomSeries.CalcXPos(ValueIndex:Integer):Integer;
Begin
  if (YMandatory) or (FStacked=cssNone) or (FStacked=cssOverlap) then
     result:=inherited CalcXPos(ValueIndex)
  else
     result:=CalcStackedPos(ValueIndex,XValues.Value[ValueIndex]);
end;

Function TCustomSeries.CalcYPos(ValueIndex:Integer):Integer;
Begin
  if (not YMandatory) or (FStacked=cssNone) or (FStacked=cssOverlap) then
     result:=inherited CalcYPos(ValueIndex)
  else
     result:=CalcStackedPos(ValueIndex,YValues.Value[ValueIndex]);
end;

function TCustomSeries.GetAreaBrush: {$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
begin
  result:=FAreaBrush.Style;
end;

procedure TCustomSeries.SetStacked(const Value: TCustomSeriesStack);

  Procedure SetOther;
  var t : Integer;
  Begin
    if Assigned(ParentChart) then
    with ParentChart do
    for t:=0 to SeriesCount-1 do
      if Self is Series[t].ClassType then
         TCustomSeries(Series[t]).FStacked:=FStacked;
  end;

Begin
  if Value<>FStacked then
  Begin
    FStacked:=Value;
    SetOther;
    Repaint;
  end;
end;

procedure TCustomSeries.SetAreaBrush(const Value: TChartBrush);
begin
  FAreaBrush.Assign(Value);
end;

procedure TCustomSeries.SetColorEachLine(const Value: Boolean);
begin
  SetBooleanProperty(FColorEachLine,Value);
end;

Procedure TCustomSeries.SetDark3D(Value:Boolean);
begin
  SetBooleanProperty(FDark3D,Value);
end;

function TCustomSeries.GetOutLine:TChartHiddenPen;
begin
  if not Assigned(FOutLine) then
     FOutLine:=TChartHiddenPen.Create(CanvasChanged);

  result:=FOutLine;
end;

procedure TCustomSeries.SetOutLine(const Value: TChartHiddenPen);
begin
  if Assigned(Value) then
     OutLine.Assign(Value)
  else
     FreeAndNil(FOutLine);
end;

function TCustomSeries.DoDrawAll(const APen:TTeePen; AColor:TColor; DrawPoints:Boolean):Boolean;

  function CalcPoints(var P:TPointArray; const AFirst,ALast:Integer):Integer;
  var t : Integer;
  begin
    result:=0;

    t:=AFirst;

    while t<=ALast do
    begin
      P[result].X:=CalcXPos(t);
      P[result].Y:=CalcYPos(t);
      Inc(result);

      if Stairs and (t<ALast) then
      begin
        if InvertedStairs then
        begin
          P[result].X:=P[result-1].X;
          P[result].Y:=CalcYPos(t+1);
        end
        else
        begin
          P[result].X:=CalcXPos(t+1);
          P[result].Y:=P[result-1].Y;
        end;

        Inc(result);
      end;

      Inc(t);
    end;
  end;

  procedure FinishAreaPolygon(const ACount:Integer; var P:TPointArray; const AOrigin:Integer);
  begin
    SetLength(P,ACount+3);

    if YMandatory then
    begin
      P[ACount+1].X:=P[ACount].X;
      P[ACount+1].Y:=AOrigin;

      P[ACount+2].X:=P[0].X;
      P[ACount+2].Y:=AOrigin;
    end
    else
    begin
      P[ACount+1].X:=AOrigin;
      P[ACount+1].Y:=P[ACount].Y;

      P[ACount+2].X:=AOrigin;
      P[ACount+2].Y:=P[0].Y;
    end;
  end;

  procedure DrawCurvePath(const P:TPointArray; const LastIndex,AOrigin:Integer);
  var tmpPath : TTeeBasePath;
  begin
    tmpPath:=ParentChart.Canvas.CreatePath;
    try
      tmpPath.Curve(P,0.5,False);

      if YMandatory then
      begin
        tmpPath.LineTo(P[LastIndex].X,AOrigin);
        tmpPath.LineTo(P[0].X,AOrigin);
      end
      else
      begin
        tmpPath.LineTo(AOrigin,P[LastIndex].Y);
        tmpPath.LineTo(AOrigin,P[0].Y);
      end;

      tmpPath.Close;

      tmpPath.Draw(ParentChart.Canvas);
    finally
      tmpPath.Free;
    end;
  end;

  procedure DrawPointsLine(const P:TPointArray);
  begin
    ParentChart.Canvas.AssignVisiblePenColor(APen,AColor);

    if DrawStyle=dsAll then
       ParentChart.Canvas.Polyline(P)
    else
    begin
      ParentChart.Canvas.Brush.Style:=bsClear;
      ParentChart.Canvas.Curve(P,0.5,False,0);
    end;
  end;

  procedure DrawAreaLines(const ACount:Integer; const P:TPointArray; const AOrigin:Integer);
  var t : Integer;
  begin
    ParentChart.Canvas.AssignVisiblePen(FAreaLinesPen);

    if YMandatory then
       for t:=1 to ACount do
           ParentChart.Canvas.VertLine3D(P[t].X,AOrigin,P[t].Y,StartZ)
    else
       for t:=1 to ACount do
           ParentChart.Canvas.HorizLine3D(AOrigin,P[t].X,P[t].Y,StartZ);
  end;

var
  tmpFractionValue : Double;

  procedure PaintLastPointer(const AIndex:Integer; const LastPoint:TPoint);

    function ColorAlpha(const AColor:TColor):TColor;
    begin
      result:=TTeeCanvas.ColorFrom(AColor,255-Round(255*tmpFractionValue));
    end;

  var tmpPenColor,
      tmpColor : TColor;
  begin
    tmpPenColor:=Pointer.Pen.Color;
    Pointer.Pen.Color:=ColorAlpha(tmpPenColor);

    tmpColor:=ValueColor[AIndex];
    tmpColor:=ColorAlpha(tmpColor);

    DrawPointer({$IFDEF FMX}Round{$ENDIF}(LastPoint.X),{$IFDEF FMX}Round{$ENDIF}(LastPoint.Y),tmpColor,AIndex);

    Pointer.Pen.Color:=tmpPenColor;
  end;

var
  tmpLast : Integer;
  tmpFraction : Boolean;

  procedure DoDrawPointers(Behind:Boolean; const LastPoint:TPoint);
  var t : Integer;
  begin
    if DrawPoints and Pointer.Visible and (Behind=PointerBehind) then
    begin
      for t:=FirstDisplayedIndex to tmpLast do
          if IsPainted(t) then
             DrawPointer(CalcXPos(t),CalcYPos(t),ValueColor[t],t);

      // Fade-In last Pointer when animating "Progressive Line" (TLineAnimation)
      if tmpFraction and (tmpLast<Count-1) then
      begin
        Inc(tmpLast);

        if IsPainted(tmpLast) then
           PaintLastPointer(tmpLast,LastPoint);
      end;
    end;
  end;

  // Conditions to enable single-pass draw of all Line series points
  function CanDrawAll:Boolean;
  begin
    result:=((not ColorEachPoint) or (not ColorEachLine)) and
            (FDrawArea or FDrawLine) and
            (DrawStyle<>dsSegments) and
            (not ParentChart.View3D);
  end;

var tmp : Integer;
    tmpFirst,
    tmpIndex : Integer;
    P : TPointArray;
    tmpOrigin : Integer;
    tmpBlend : TTeeBlend;
    tmpValue : Double;
begin
  result:=False;

  if CanDrawAll then
  begin
    if Transparency>0 then
       tmpBlend:=ParentChart.Canvas.BeginBlending(TeeZeroRect, Transparency)
    else
       tmpBlend:=nil;

    tmpFirst:=FirstDisplayedIndex;

    if tmpFirst>0 then
       Dec(tmpFirst);

    tmpFractionValue:=0;

    if DisplayLast=1 then
    begin
      tmpLast:=LastDisplayedIndex;
      tmpFraction:=False;
    end
    else
    begin
      tmpLast:=Min(Count-1,Trunc(DisplayLast*Count));

      if tmpLast<tmpFirst then
         tmpLast:=tmpFirst
      else
      if tmpLast<Count-1 then
         tmpFractionValue:=(DisplayLast*Count)-Trunc(DisplayLast*Count);

      tmpFraction:=tmpFractionValue>0;
    end;

    tmp:=tmpLast-tmpFirst;

    if (tmp>0) or tmpFraction then
    begin
      if Stairs then
         tmp:=tmp*2;

      if tmpFraction then
         SetLength(P,tmp+2)
      else
         SetLength(P,tmp+1);

      tmpIndex:=CalcPoints(P,tmpFirst,tmpLast);

      if tmpFraction then
      begin
        tmpValue:=XValues.Value[tmpLast]+tmpFractionValue;
        P[tmpIndex].X:=CalcXPosValue(tmpValue);
        P[tmpIndex].Y:=CalcPosLeftRight(tmpValue,tmpIndex-1,tmpIndex);
      end
      else
        Dec(tmpIndex);

      DoDrawPointers(True,P[tmpIndex]);

      if FDrawArea then
      begin
        tmpOrigin:=GetOriginPos(tmpLast);

        ParentChart.Canvas.AssignVisiblePen(APen);
        ParentChart.Canvas.AssignBrush(FAreaBrush,Color);

        if tmpFraction then
           Inc(tmp);

        if DrawStyle=dsAll then
        begin
          FinishAreaPolygon(tmp,P,tmpOrigin);
          ParentChart.Canvas.PolygonWithZ(P,StartZ);
        end
        else
          DrawCurvePath(P,tmp,tmpOrigin);

        if FAreaLinesPen.Visible then
           DrawAreaLines(tmp,P,tmpOrigin);
      end
      else
        DrawPointsLine(P);

      DoDrawPointers(False,P[tmpIndex]);
    end;

    if Transparency>0 then
       ParentChart.Canvas.EndBlending(tmpBlend);

    result:=True;
  end;
end;

type
  TCustomAxisPanelClass = class(TCustomAxisPanel);

procedure TCustomSeries.DrawAllValues;

  procedure DrawOutline;
  var oldLinePen : TChartPen;
      oldOutLine : TChartHiddenPen;
      tmpColor   : TColor;
  begin
    oldLinePen:=TChartPen.Create(nil);
    oldOutLine:=TChartHiddenPen.Create(nil);
    try
      oldLinePen.Assign(LinePen);

      if not oldLinePen.Visible then
         oldLinePen.Width:=1; //TF02014533

      oldOutLine.Assign(OutLine);

      tmpColor:=SeriesColor;
      SeriesColor:=OutLine.Color;

      LinePen:=OutLine;
      LinePen.Width:=2*LinePen.Width+oldLinePen.Width;

      inherited;

      LinePen:=oldLinePen;
      OutLine:=oldOutLine;

      if not ParentChart.View3D then
         SeriesColor:=LinePen.Color // TV52010022
      else
         SeriesColor:=tmpColor;

    finally
      oldLinePen.Free;
      oldOutLine.Free;
    end;
  end;

  procedure DrawShadowEmboss(const AShadow:TTeeShadow; Factor:Integer);

    procedure OffsetAxes(ASign:Integer);
    var tmp : Integer;
    begin
      tmp:=ASign*Factor*AShadow.HorizSize;
      Inc(GetHorizAxis.IStartPos,tmp);
      Inc(GetHorizAxis.IEndPos,tmp);

      tmp:=ASign*Factor*AShadow.VertSize;
      Inc(GetVertAxis.IStartPos,tmp);
      Inc(GetVertAxis.IEndPos,tmp);
    end;

  var tmpPointer: Boolean;
      tmpSize   : Integer;
      tmpColor  : TColor;
      tmpEach   : Boolean;
      tmpBorder : Boolean;
  begin
    if AShadow.Visible then
       tmpSize:=AShadow.Size
    else
       tmpSize:=0;

    if tmpSize<>0 then
    begin
      tmpColor:=SeriesColor;
      SeriesColor:=AShadow.Color;

      tmpEach:=ColorEachLine;
      ColorEachLine:=False;

      tmpPointer:=Pointer.Visible;
      Pointer.Visible:=False;

      tmpBorder:=Pen.Visible;
      Pen.Hide;

      OffsetAxes(1);
      inherited;
      OffsetAxes(-1);

      ColorEachLine:=tmpEach;
      Pointer.Visible:=tmpPointer;
      SeriesColor:=tmpColor;
      Pen.Visible:=tmpBorder;
    end;
  end;

  procedure DrawTemplate;
  var tmpR : TRect;
  begin
    TVisualAccess(Visuals.Template).IsCloneable:=False;

    tmpR:=TeeRect(CalcXPos(FirstValueIndex),CalcPosValue(MandatoryValueList.MaxValue),
                  CalcXPos(LastValueIndex),CalcPosValue(MandatoryValueList.MinValue));

    TCanvas3DAccess(ParentChart.Canvas).BeginEntity('',Visuals.Template);
    TVisualAccess(Visuals.Template).SetBounds(tmpR,StartZ,EndZ,ParentChart.Height);
    TVisualAccess(Visuals.Template).SetFormatting(Pen,Brush,False,SeriesColor,Transparency);

    TVisualAccess(Visuals.Template).ClearPath;
  end;

var OldFastCalc : Boolean;
begin
  IClientRect:=ParentChart.ChartBounds;

  //TV52014805: Line series X/Y limits will be checked both using CheckMaxPixelXY
  //instead of using the checking performed by axis.
  //MM Note. AxisBreaks tool (using FOnDrawValue) does not currently support FastCalc.

  OldFastCalc:=ParentChart.Axes.FastCalc;

  if FDrawLine and (not Assigned(TCustomAxisPanelClass(ParentChart).FOnDrawValue)) then
     ParentChart.Axes.FastCalc:=True;

  HasTemplate:=Assigned(Visuals.Template);

  if HasTemplate and (DrawArea or FDrawLine) then  // Experimental
     DrawTemplate
  else
  begin
    DrawShadowEmboss(Shadow,1);
    DrawShadowEmboss(Emboss,-1);

    if Assigned(FOutLine) and FOutLine.Visible then
       if not DoDrawAll(FOutLine,FOutLine.Color,False) then
          DrawOutline;
  end;

  // Safety check.
  if (not ParentChart.View3D) and FDrawLine and (not Pen.Visible) and (not Pointer.Visible) then
     if not FDrawArea then
        Pen.Show;

  if not DoDrawAll(Pen,Color,True) then
  begin
    ParentChart.Canvas.AssignBrush(Brush);
    inherited;
  end;

  if ParentChart.Axes.FastCalc<>OldFastCalc then
     ParentChart.Axes.FastCalc:=OldFastCalc;

  {$IFNDEF TEELITEEMB}
  // Moved to DoBeforeDrawChart
  // SmoothSeries;
  {$ENDIF}
end;

procedure TCustomSeries.SetGradient(const Value: TTeeGradient);
begin
  Brush.Gradient.Assign(Value);
end;

function TCustomSeries.GetGradient: TTeeGradient;
begin
  if Self is TAreaSeries then
     result:=AreaChartBrush.Gradient
  else
     result:=Brush.Gradient;
end;

{ TLineSeries }

Constructor TLineSeries.Create(AOwner: TComponent);
Begin
  inherited;

  FDrawLine:=True;
  AllowSinglePoint:=False;

  FPointer.Hide;
  FPointer.DefaultVisible:=False;

  IMandatoryPen2D:=True;
  DrawBetweenPoints:=True;
end;

Procedure TLineSeries.PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                           var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
Begin
  ParentChart.Canvas.AssignVisiblePen(LinePen);
  BrushStyle:=LineBrush;
end;

Procedure TLineSeries.Assign(Source:TPersistent);
begin
  inherited;

  if (not (Source is Self.ClassType)) and
     (not (Source is TLineSeries)) then
         FPointer.Hide;

  FDrawArea:= False;
  FDrawLine:= True;
end;

class Procedure TLineSeries.SetSubGallery(ASeries:TChartSeries; Index:Integer);
begin
  With TLineSeries(ASeries) do
  Case Index of
    1: Stairs:=True;
    2: Pointer.Visible:=True;
    3: LineHeight:=5;
    4: LineBrush:=bsClear;
    5: ColorEachPoint:=True;
    6: Marks.Visible:=True;
    7: Pen.Hide;
    8: OutLine.Show;
  end;
end;

class Procedure TLineSeries.CreateSubGallery(AddSubChart:TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Stairs);
  AddSubChart(TeeMsg_Points);
  AddSubChart(TeeMsg_Height);
  AddSubChart(TeeMsg_Hollow);
  AddSubChart(TeeMsg_Colors);
  AddSubChart(TeeMsg_Marks);
  AddSubChart(TeeMsg_NoBorder);
  AddSubChart(TeeMsg_Outline);
end;

{ THorizLineSeries }
Constructor THorizLineSeries.Create(AOwner: TComponent);
begin
  inherited;
  SetHorizontal;
  CalcVisiblePoints:=False; { avoid bug first point and scroll }
  XValues.Order:=loNone;
  YValues.Order:=loAscending;
end;

Function THorizLineSeries.CalcMarkLength:Integer;
begin
  result:=MaxMarkWidth;
end;

Procedure THorizLineSeries.DrawMark( ValueIndex:Integer; Const St:String;
                                     APosition:TSeriesMarkPosition);
var DifW : Integer;
    tmp  : Boolean;
Begin
  With APosition do
  begin
    DifW:=Marks.Callout.Length+Marks.Callout.Distance;
    tmp:=ArrowFrom.X<GetOriginPos(ValueIndex);
    if tmp then DifW:=-DifW-Width;

    {$IFDEF FMX}
    LeftTop.X:=LeftTop.X+DifW+Width*0.5;
    LeftTop.Y:=LeftTop.Y+Height*0.5;
    {$ELSE}
    Inc(LeftTop.X,DifW+(Width div 2));
    Inc(LeftTop.Y,Height div 2);
    {$ENDIF}

    ArrowTo.X:=ArrowTo.X+DifW;

    if tmp then
       ArrowFrom.X:=ArrowFrom.X-Marks.Callout.Distance
    else
       ArrowFrom.X:=ArrowFrom.X+Marks.Callout.Distance;
  end;

  inherited;
end;

type
  TCalloutAccess=class(TMarksCallout);

{ TPointSeries }
Constructor TPointSeries.Create(AOwner: TComponent);
Begin
  inherited;
  SetFixed;
  Marks.Callout.Length:=0;
  TCalloutAccess(Marks.Callout).DefaultLength:=0;
end;

Procedure TPointSeries.Assign(Source:TPersistent);
begin
  inherited;
  SetFixed;
end;

Procedure TPointSeries.SetFixed;
begin
  FPointer.Visible:=True;
  FDrawArea:=False;
  FDrawLine:=False;
  ClickableLine:=False;
end;

class Function TPointSeries.GetEditorClass:String;
begin
  result:='TSeriesPointerEditor'; // Do not localize
end;

Procedure TPointSeries.PrepareForGallery;
var tmp : Integer;
begin
  inherited;

  With ParentChart do
  begin
    if Width<50 then tmp:=4
                else tmp:=6;

    Pointer.HorizSize:=tmp;
    Pointer.VertSize:=tmp;

    if (SeriesCount>1) and (Self=Series[1]) then
       Pointer.Style:=psTriangle;
  end;
end;

Procedure TPointSeries.SetColorEachPoint(Value:Boolean);
begin
  inherited;

  if Value then
     Pointer.Color:=clTeeColor;
end;

class Procedure TPointSeries.CreateSubGallery(AddSubChart:TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Colors);
  AddSubChart(TeeMsg_Marks);
  AddSubChart(TeeMsg_Hollow);
  AddSubChart(TeeMsg_NoBorder);
  AddSubChart(TeeMsg_Gradient);

  if CanDoExtra then
  begin
    AddSubChart(TeeMsg_Point2D);
    AddSubChart(TeeMsg_Triangle);
    AddSubChart(TeeMsg_Star);
    AddSubChart(TeeMsg_Circle);
    AddSubChart(TeeMsg_DownTri);
    AddSubChart(TeeMsg_Cross);
    AddSubChart(TeeMsg_Diamond);
    AddSubChart(TeeMsg_Hexagon);
    AddSubChart(TeeMsg_GalleryDonut);
    AddSubChart(TeeMsg_GalleryArrow);
  end;
end;

class Procedure TPointSeries.SetSubGallery(ASeries:TChartSeries; Index:Integer);
begin
  With TPointSeries(ASeries) do
  Case Index of
    1: ColorEachPoint:=True;
    2: Marks.Visible:=True;
    3: Pointer.Brush.Style:=bsClear;
    4: Pointer.Pen.Hide;
    5: Pointer.Gradient.Visible:=True;
  else
  if CanDoExtra then
    Case Index of
      6: Pointer.Draw3D:=False;
      7: Pointer.Style:=psTriangle;
      8: Pointer.Style:=psStar;
      9: With Pointer do
         begin
           Style:=psCircle;
           HorizSize:=8;
           VertSize:=8;
         end;
     10: Pointer.Style:=psDownTriangle;
     11: Pointer.Style:=psCross;
     12: Pointer.Style:=psDiamond;
     13: Pointer.Style:=psHexagon;
     14: Pointer.Style:=psDonut;
     15: Pointer.Style:=psArrow;
    end;
  end;
end;

// For sub-gallery only. 
class function TPointSeries.CanDoExtra: Boolean;
begin
  result:=True;
end;

procedure TPointSeries.SetTransparency(const Value: TTeeTransparency);
begin
  inherited;
  Pointer.Transparency:=Value;
end;

{ TAreaSeries }
Constructor TAreaSeries.Create(AOwner: TComponent);
Begin
  inherited;
  FDrawArea:=True;
  AllowSinglePoint:=False;
  FPointer.Hide;
  DrawBetweenPoints:=True;
end;

Procedure TAreaSeries.PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                           var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
Begin
  BackColor:=GetAreaBrushColor(ParentChart.Canvas.Brush.Color);
  ParentChart.Canvas.AssignVisiblePen(FAreaLinesPen);
  BrushStyle:=FAreaBrush.Style;
end;

procedure TAreaSeries.ResetBrush;
begin
  inherited;

  AreaChartBrush.Style:=bsSolid;
  AreaChartBrush.Color:=clTeeColor;
  AreaChartBrush.BackColor:=clTeeColor;
end;

Procedure TAreaSeries.Assign(Source:TPersistent);
begin
  inherited;

  FDrawArea:=True;
  FDrawLine:=True;

  if SameClass(Source as TChartSeries) then
  with TAreaSeries(Source) do
  begin
    Self.FOrigin    :=YOrigin;
    Self.FStackGroup:=StackGroup;
    Self.FUseOrigin :=UseYOrigin;
  end
  else FPointer.Hide;
end;

class Function TAreaSeries.GetEditorClass:String;
Begin
  result:='TAreaSeriesEditor'; { <-- dont translate ! }
end;

function TAreaSeries.GetMultiArea: TMultiArea;
begin
  Case Stacked of
    cssStack: result:=maStacked;
    cssStack100: result:=maStacked100;
  else
    result:=maNone;
  end;
end;

Procedure TAreaSeries.SetMultiArea(Value:TMultiArea);
Begin
  if Value<>MultiArea then
  Case Value of
    maNone      : Stacked:=cssNone;
    maStacked   : Stacked:=cssStack;
    maStacked100: Stacked:=cssStack100;
  end;
End;

function TAreaSeries.SameClassOrigin(ASeries:TChartSeries):Boolean;
begin
  result:=inherited SameClassOrigin(ASeries) and
          (TAreaSeries(ASeries).StackGroup=StackGroup);
end;

type
  TSeriesAccess=class(TChartSeries);

Procedure TAreaSeries.CalcZOrder;
var t   : Integer;
    tmp : TChartSeries;
    tmpZOrder : Integer;
    Old : TCustomSeriesStack;
begin
  if MultiArea=maNone then
     inherited
  else
  begin
    // Copy ZOrder from previous active Area series with same StackGroup...

    tmpZOrder:=-1;

    with ParentChart do
    for t:=0 to SeriesCount-1 do
    begin
      tmp:=Series[t];

      if tmp.Active then
      begin
        if tmp=Self then
           break
        else
        if SameClassOrigin(tmp) then
        begin
          tmpZOrder:=tmp.ZOrder;
          break;
        end;
      end;
    end;

    if tmpZOrder=-1 then
    begin
      Old:=FStacked;
      FStacked:=cssNone;
      try
        inherited;
      finally
        FStacked:=Old;
      end;
    end
    else
       IZOrder:=tmpZOrder;
  end;
end;

class Procedure TAreaSeries.CreateSubGallery(AddSubChart:TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Stairs);
  AddSubChart(TeeMsg_Marks);
  AddSubChart(TeeMsg_Colors);
  AddSubChart(TeeMsg_Hollow);
  AddSubChart(TeeMsg_NoLines);
//  AType.NumGallerySeries:=2;
  AddSubChart(TeeMsg_Stack);
  AddSubChart(TeeMsg_Stack100);
//  AType.NumGallerySeries:=1;
  AddSubChart(TeeMsg_Points);
  AddSubChart(TeeMsg_Gradient);
end;

class Procedure TAreaSeries.SetSubGallery(ASeries:TChartSeries; Index:Integer);
begin
  With TAreaSeries(ASeries) do
  Case Index of
    1: Stairs:=True;
    2: Marks.Visible:=True;
    3: ColorEachPoint:=True;
    4: AreaBrush:=bsClear;
    5: AreaLinesPen.Hide;
    6: MultiArea:=maStacked;
    7: MultiArea:=maStacked100;
    8: Pointer.Visible:=True;
    9: Gradient.Visible:=True;
  end;
end;

Function TAreaSeries.DrawSeriesForward(ValueIndex:Integer):Boolean;
begin
  if MultiArea=maNone then
     result:=inherited DrawSeriesForward(ValueIndex)
  else
     result:=not MandatoryAxis.Inverted;
end;

procedure TAreaSeries.SetOrigin(const Value: Double);
begin
  SetDoubleProperty(FOrigin,Value)
end;

Procedure TAreaSeries.SetStackGroup(const Value:Integer);
begin
  SetIntegerProperty(FStackGroup,Value);
end;

procedure TAreaSeries.SetUseOrigin(Value: Boolean);
begin
  SetBooleanProperty(FUseOrigin,Value)
end;

function TAreaSeries.MaxXValue: TChartValue;
begin
  result:=inherited MaxXValue;
  if (not YMandatory) and UseYOrigin and (result<YOrigin) then result:=YOrigin;
end;

function TAreaSeries.MinXValue: TChartValue;
begin
  result:=inherited MinXValue;
  if (not YMandatory) and UseYOrigin and (result>YOrigin) then result:=YOrigin;
end;

function TAreaSeries.MaxYValue: TChartValue;
begin
  result:=inherited MaxYValue;
  if YMandatory and UseYOrigin and (result<YOrigin) then result:=YOrigin;
end;

function TAreaSeries.MinYValue: TChartValue;
begin
  result:=inherited MinYValue;
  if YMandatory and UseYOrigin and (result>YOrigin) then result:=YOrigin;
end;

function TAreaSeries.GetOriginPos(ValueIndex: Integer): Integer;
begin
  if UseYOrigin then result:=CalcPosValue(YOrigin)
                else result:=inherited GetOriginPos(ValueIndex);
end;

procedure TAreaSeries.DrawLegendShape(ValueIndex: Integer; const Rect: TRect);
var tmpColor : TColor;
    tmpBack  : TColor;
begin
  if Gradient.Visible then // TV52015411
     ParentChart.SetBrushCanvas(SeriesColor,AreaChartBrush,Brush.BackColor)
  else
     ParentChart.SetBrushCanvas(SeriesColor,Brush,Brush.Color);

  if FAreaBrush.Image.Valid then // TV52010270
  begin
    tmpColor:=FAreaBrush.Color;
    if tmpColor=clTeeColor then
       tmpColor:=LegendItemColor(ValueIndex);

    tmpBack:=GetAreaBrushColor(ParentChart.Canvas.Brush.Color);
    if tmpColor=tmpBack then
       tmpBack:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};

    ParentChart.Canvas.AssignBrush(FAreaBrush,tmpColor,tmpBack);
  end;

  inherited;
end;

{ THorizAreaSeries }

Constructor THorizAreaSeries.Create(AOwner: TComponent);
begin
  inherited;
  SetHorizontal;

  //  CalcVisiblePoints:=False; { avoid bug first point and scroll }

  XValues.Order:=loNone;
  YValues.Order:=loAscending;
  Gradient.Direction:=gdRightLeft;
end;

procedure THorizAreaSeries.DrawMark(ValueIndex: Integer; const St: String;
  APosition: TSeriesMarkPosition);
var DifH : Integer;
    DifW : Integer;
begin
  With APosition do
  begin
    DifH:=Height div 2;

    DifW:=Marks.Callout.Length+Marks.Callout.Distance;

    LeftTop.Y:=ArrowTo.Y-DifH;

    LeftTop.X:=LeftTop.X+DifW+(Width div 2);

    ArrowTo.X:=ArrowTo.X+DifW;

    ArrowFrom.Y:=ArrowTo.Y;
    ArrowFrom.X:=ArrowFrom.X+Marks.Callout.Distance;
  end;

  inherited;
end;

function THorizAreaSeries.NumSampleValues: Integer;
begin
  result:=10;
end;

{ TSelfStack }

procedure TSelfStack.SetUsePosition(const Value:Boolean);
begin
  TSeriesAccess(IParent).SetBooleanProperty(FUsePosition,Value);
end;

procedure TSelfStack.SetPosition(const Value:TChartValue);
begin
  if FPosition<>Value then
  begin
    FPosition:=Value;
    IParent.Repaint;
  end;
end;

procedure TSelfStack.Assign(Source:TPersistent);
begin
  if Source is TSelfStack then
  begin
    FPosition:=TSelfStack(Source).FPosition;
    FUsePosition:=TSelfStack(Source).FUsePosition;
  end
  else
    inherited;
end;

type
  TShapeAccess=class(TTeeCustomShapeBrushPen);
  TShadowAccess=class(TTeeShadow);
  TEmbossAccess=class(TTeeEmboss);

{ TCustomBarSeries }

Constructor TCustomBarSeries.Create(AOwner:TComponent);
Begin
  inherited;

  FBarWidthPercent:=70;
  FDepthPercent:=100;

  FBarRound:=brAtValue;
  FRoundSize:=16;

  FBarStyle:=bsRectangle;

  FSides:=32;

  Gradient.Visible:=False;

  FBevelSize:=1;
  FUseOrigin:=True;
  FMultiBar:=mbSide;

  Marks.Visible:=True;
  TShapeAccess(Marks).DefaultVisible:=True;

  Marks.OnTop:=True;

  Marks.Callout.Length:=20;
  TCalloutAccess(Marks.Callout).DefaultLength:=20;

  FDark3D:=True;
  FSideMargins:=True;
  FMarksLocation:=mlEnd;

  FEmboss:=TTeeEmboss.Create(CanvasChanged);
  TEmbossAccess(FEmboss).InitValues(FEmboss.Visible,
         {$IFDEF FMX}TAlphaColors.DarkGray{$ELSE}clDkGray{$ENDIF},FEmboss.Size);

  FShadow:=TTeeShadow.Create(CanvasChanged);
  TShadowAccess(FShadow).InitValues(FShadow.Visible,
         {$IFDEF FMX}TAlphaColors.DarkGray{$ELSE}clDkGray{$ENDIF},FShadow.Size);

  FSelfStack:=TSelfStack.Create;
  FSelfStack.IParent:=Self;
end;

Destructor TCustomBarSeries.Destroy;
begin
  FSelfStack.Free;
  FEmboss.Free;
  FShadow.Free;
  FTickLines.Free;

  inherited;
end;

Procedure TCustomBarSeries.CalcZOrder;
var t         : Integer;
    tmpZOrder : Integer;
    tmp       : TChartSeries;
Begin
  if FMultiBar=mbNone then
     inherited
  else
  begin
    tmpZOrder:=-1;

    // Copy ZOrder from previous active CustomBar series...
    with ParentChart do
    for t:=0 to SeriesCount-1 do
    begin
      tmp:=Series[t];

      if tmp.Active then
      begin
        if tmp=Self then break
        else
        if SameClass(tmp) then
        Begin
          tmpZOrder:=tmp.ZOrder;
          break;
        end;
      end;
    end;

    if tmpZOrder=-1 then inherited
                    else IZOrder:=tmpZOrder;
  end;
End;

procedure TCustomBarSeries.CalcDepthPositions;
var tmp : Integer;
begin
  inherited;

  if DepthPercent<>0 then
  begin
    tmp:=Round((EndZ-StartZ)*(100-DepthPercent)*0.005);
    StartZ:=StartZ+tmp;
    EndZ:=EndZ-tmp;
  end;
end;

Function TCustomBarSeries.MoreSameZOrder:Boolean;
begin
  if (FMultiBar=mbSideAll) or (FMultiBar=mbSelfStack) then
     result:=False
  else
     result:=inherited MoreSameZOrder;
end;

Procedure TCustomBarSeries.CalcFirstLastVisibleIndex;
begin
  if FMultiBar=mbSelfStack then
  begin
    FFirstVisibleIndex:=0;
    FLastVisibleIndex:=Count-1;
  end
  else
  if ParentChart.MaxPointsPerPage=1 then
  begin

    // Very special case, only for THorizBar and TBar series
    FFirstVisibleIndex:=ParentChart.Pages.Current-1;

    if FFirstVisibleIndex>=Count then
       FFirstVisibleIndex:=-1;

    FLastVisibleIndex:=FFirstVisibleIndex;
  end
  else
  begin
    inherited;
    if ParentChart.MaxPointsPerPage=0 then
    begin
      while (FLastVisibleIndex < Count - 1) and IsPointInChartRect(FLastVisibleIndex + 1) do Inc(FLastVisibleIndex);
      while (FLastVisibleIndex > -1) and (FLastVisibleIndex < Count - 1) and
        (NotMandatoryValueList.Items[FLastVisibleIndex] = NotMandatoryValueList.Items[FLastVisibleIndex+1]) do Inc(FLastVisibleIndex);

      while (FFirstVisibleIndex > 0) and IsPointInChartRect(FFirstVisibleIndex - 1) do Dec(FFirstVisibleIndex);
      while (FFirstVisibleIndex > 0) and
        (NotMandatoryValueList.Items[FFirstVisibleIndex] = NotMandatoryValueList.Items[FFirstVisibleIndex-1]) do Dec(FFirstVisibleIndex);
    end;
  end;
end;

Function TCustomBarSeries.NumSampleValues:Integer;
var t : Integer;
Begin
  result:=6; { default number of BarSeries random sample values }
  if Assigned(ParentChart) and (ParentChart.SeriesCount>1) then
     for t:=0 to ParentChart.SeriesCount-1 do
     if (ParentChart[t]<>Self) and
        (ParentChart[t] is TCustomBarSeries) and
        (ParentChart[t].Count>0) then
     begin
       result:=ParentChart[t].Count;
       break;
     end;
end;

Procedure TCustomBarSeries.SetBarWidthPercent(Value:Integer);
Begin
  SetIntegerProperty(FBarWidthPercent,Value);
end;

Procedure TCustomBarSeries.SetDepthPercent(Value:Integer);
Begin
  SetIntegerProperty(FDepthPercent,Value);
end;

Procedure TCustomBarSeries.SetOffsetPercent(Value:Integer);
Begin
  SetIntegerProperty(FOffsetPercent,Value);
End;

Procedure TCustomBarSeries.SetCustomBarSize(Value:Integer);
Begin
  SetIntegerProperty(FCustomBarSize,Value);
end;

Procedure TCustomBarSeries.SetParentChart(Const Value:TCustomAxisPanel);
begin
  inherited;
  SetOtherBars(False);
end;

Procedure TCustomBarSeries.SetBarRound(Value:TBarRound);
Begin
  if FBarRound<>Value then
  begin
    FBarRound:=Value;
    Repaint;
  end;
end;

Procedure TCustomBarSeries.SetBarStyle(Value:TBarStyle);
Begin
  if FBarStyle<>Value then
  begin
    FBarStyle:=Value;

    // 457
    if FBarStyle=bsRectangle then
       Gradient.Visible:=False;
       
    Repaint;
  end;
end;

type
  TIntegerList=TList{$IFDEF D18}<Integer>{$ENDIF};

Function TCustomBarSeries.SortDrawPoints(FirstPoint:Integer;
                            LastPoint:Integer; SingleSeries:Boolean):TIntArray;

  procedure DoSortSelfStacked;
  var nPoints : TIntegerList;
      pPoints : TIntegerList;

      procedure TryAdd(const AList:TChartValueList; Index:Integer);
      begin
        if AList.Value[Index] < YOrigin then
        begin
            if nPoints.IndexOf({$IFNDEF D18}Pointer{$ENDIF}(Index))=-1 then
               nPoints.Add({$IFNDEF D18}Pointer{$ENDIF}(Index));
        end
        else
            if pPoints.IndexOf({$IFNDEF D18}Pointer{$ENDIF}(Index))=-1 then
               pPoints.Add({$IFNDEF D18}Pointer{$ENDIF}(Index));
      end;

  var i       : Integer;
      t       : Integer;
      count   : Integer;
      tmp     : TIntArray;
      tmpAxis : TChartAxis;
  begin
    nPoints:=TIntegerList.Create;
    pPoints:=TIntegerList.Create;

    if SingleSeries then
       for i:=FirstPoint to LastPoint do
           TryAdd(MandatoryValueList,i)
    else
       for i:=FirstPoint to LastPoint do
           for t:=0 to ParentChart.SeriesCount-1 do
               if ParentChart[t].Active and (ParentChart[t].ZOrder=ZOrder)
                  and (i<ParentChart[t].Count) then
                      TryAdd(ParentChart[t].MandatoryValueList, i);

    count:=0;
    SetLength(tmp, nPoints.Count + pPoints.Count);

    if YMandatory then tmpAxis:=GetHorizAxis
                  else tmpAxis:=GetVertAxis;

    if tmpAxis.Inverted then
    begin
      if pPoints.Count>0 then
        for i:=pPoints.Count-1 downto 0 do
        begin
          tmp[count]:=Integer(pPoints[i]);
          Inc(count);
        end;

      if nPoints.Count>0 then
        for i:=0 to nPoints.Count-1 do
        begin
          tmp[count]:=Integer(nPoints[i]);
          Inc(count);
        end
    end
    else
    begin
      if nPoints.Count>0 then
        for i:=nPoints.Count-1 downto 0 do
        begin
          tmp[count]:=Integer(npoints[i]);
          Inc(count);
        end;

      if pPoints.Count>0 then
        for i:=0 to pPoints.Count-1 do
        begin
          tmp[count]:=Integer(pPoints[i]);
          Inc(count);
        end
    end;

    pPoints.Free;
    nPoints.Free;
    
    result:=tmp;
  end;

begin
  if FMultiBar=mbSelfStack then
     DoSortSelfStacked
  else
     result:=nil;
end;

Function TCustomBarSeries.SortDrawSeries(FirstSeries: Integer;
                LastSeries: Integer; ValueIndex: Integer):TIntArray;

  procedure DoSortStacked;
  var allSeries : TIntegerList;
      i         : Integer;
      count     : Integer;

    function AddSorted(AList: TIntegerList; AIndex: Integer): TIntegerList;
    var i: Integer;
    begin
      result:=AList;

      if not (ParentChart[AIndex] is TCustomBarSeries) then
      begin
        result.Add({$IFNDEF D18}Pointer{$ENDIF}(AIndex));
        Exit;
      end;

      if ParentChart[AIndex].MandatoryValueList[ValueIndex] < YOrigin then
      begin
        for i:=0 to result.Count-1 do
          if (ParentChart[Integer(AList[i])] is TCustomBarSeries) and
             ((ParentChart[AIndex] as TCustomBarSeries).StackGroup =
             (ParentChart[Integer(AList[i])] as TCustomBarSeries).StackGroup) then
          begin
             result.Insert(i, {$IFNDEF D18}Pointer{$ENDIF}(AIndex));
             Exit;
          end;
      end
      else
        for i:=result.Count-1 downto 0 do
          if (ParentChart[Integer(AList[i])] is TCustomBarSeries) and
             ((ParentChart[AIndex] as TCustomBarSeries).StackGroup =
             (ParentChart[Integer(AList[i])] as TCustomBarSeries).StackGroup) then
          begin
             result.Insert(i+1, {$IFNDEF D18}Pointer{$ENDIF}(AIndex));
             Exit;
          end;

      result.Add({$IFNDEF D18}Pointer{$ENDIF}(AIndex));
    end;

  begin
    allSeries:=TIntegerList.Create;

    for i:=FirstSeries to LastSeries-1 do
    with ParentChart[i] do
         if Active and (Count>ValueIndex) then
            allSeries:=AddSorted(allSeries, i);

    count:=0;

    SetLength(result, allSeries.Count);
    for i:=0 to allSeries.Count-1 do
    begin
      result[count]:=Integer(allSeries[i]);
      Inc(count);
    end;

    allSeries.Free;
  end;

begin
  Case FMultiBar of
    mbStacked,
    mbStacked100: DoSortStacked;
  else
    result:=inherited SortDrawSeries(FirstSeries, LastSeries, ValueIndex);
  end;
end;

{ If more than one bar series exists in chart,
  which position are we? the first, the second, the third? }
Procedure TCustomBarSeries.DoBeforeDrawChart;
var Groups : TIntArray;

  function NewGroup(const AGroup:Integer):Boolean;
  var t : Integer;
  begin
    for t:=0 to Length(Groups)-1 do
    if Groups[t]=AGroup then
    begin
      result:=False;
      exit;
    end;

    SetLength(Groups,Length(Groups)+1);
    Groups[Length(Groups)-1]:=AGroup;
    result:=True;
  end;

  // Returns True when both Self and ASeries mandatory axis is "the same".
  function SameMandatoryAxis(const ASeries:TChartSeries):Boolean;
  begin
    if YMandatory then
       result:=((VertAxis<>aCustomVertAxis) and (ASeries.VertAxis<>aCustomVertAxis))
                or (ASeries.GetVertAxis=GetVertAxis)
    else
       result:=((HorizAxis<>aCustomHorizAxis) and (ASeries.HorizAxis<>aCustomHorizAxis))
               or (ASeries.GetHorizAxis=GetHorizAxis);
  end;

var t    : Integer;
    Stop : Boolean;
    tmp  : Integer;
begin
  inherited;

  IBarSize:=0;
  IOrderPos:=1;
  IPreviousCount:=0;
  INumBars:=0;
  IMaxBarPoints:=TeeAllValues;
  Groups:=nil;
  Stop:=False;

  With ParentChart do
  for t:=0 to SeriesCount-1 do
  if Series[t].Active then
  begin
    if SameClass(Series[t]) and SameMandatoryAxis(Series[t]) then
    begin
      Stop:=Stop or (Series[t]=Self);
      tmp:=Series[t].Count;

      if (IMaxBarPoints=TeeAllValues) or (tmp>IMaxBarPoints) then
      begin
        IMaxBarPoints:=tmp;

        if FSideMargins and (tmp>0) then
           Inc(IMaxBarPoints);
      end;

      Case FMultiBar of
        mbNone: INumBars:=1;
        mbSide,
        mbSideAll,
        mbSelfStack:
                   begin
                     Inc(INumBars);
                     if not Stop then Inc(IOrderPos);
                   end;
        mbStacked,
        mbStacked100: if NewGroup(TCustomBarSeries(Series[t]).FStackGroup) then
                      begin
                        Inc(INumBars);
                        if not Stop then Inc(IOrderPos);
                      end;
      end;

      if not Stop then
         Inc(IPreviousCount,tmp);
    end;
  end;

  for t:=0 to Length(Groups)-1 do
  if Groups[t]=FStackGroup then
  begin
    IOrderPos:=t+1;
    break;
  end;

  Groups:=nil;

  if INumBars=0 then
     INumBars:=1;
     
  { this should be after calculating INumBars }
  if FMultiBar=mbSelfStack then
     if ParentChart.MaxPointsPerPage=0 then
        IMaxBarPoints:=INumBars
     else
        IMaxBarPoints:=ParentChart.MaxPointsPerPage

  else
  if ParentChart.MaxPointsPerPage>0 then
     IMaxBarPoints:=ParentChart.MaxPointsPerPage;

  if (ParentChart.MaxPointsPerPage>0) and FSideMargins and (IMaxBarPoints>0) then
     Inc(IMaxBarPoints);
end;

Procedure TCustomBarSeries.DoBeforeDrawValues;
begin
  inherited;

  // When non-mandatory axis is Automatic (Min and/or Max), IBarSize will not be zero,
  // as it will be already calculated when calculating margins:

  if IBarSize=0 then
     CalcBarWidth;
end;

function TCustomBarSeries.DrawValuesForward:Boolean;
begin
  if FMultiBar=mbSelfStack then
     result:=not MandatoryAxis.Inverted
  else
     result:=inherited DrawValuesForward;
end;

type
  TTeePanelAccess=class(TCustomTeePanel);

Function TCustomBarSeries.CalcMarkLength(ValueIndex:Integer):Integer;
Begin
  if (Count>0) and Marks.Visible then
  Begin
    TTeePanelAccess(ParentChart).TryAssignFont(Marks.Font);
    result:=Marks.Callout.Length+InternalCalcMarkLength(ValueIndex);

    with Marks.Frame do
    if Visible then
       result:=result+{$IFDEF FMX}Round{$ENDIF}(2*Width);
  end
  else
    result:=0;
end;

Procedure TCustomBarSeries.DrawMark( ValueIndex:Integer; Const St:String;
                                     APosition:TSeriesMarkPosition);
var Old : Boolean;
begin
  Old:=Marks.Automatic.Move;
  if MarksOnBar then
     Marks.Automatic.Move:=False;

  inherited;

  if MarksOnBar then
     Marks.Automatic.Move:=Old;

  // Debug
  {
  ParentChart.Canvas.Brush.Style:=bsClear;
  ParentChart.Canvas.Pen.Color:=clRed;
  ParentChart.Canvas.Rectangle(APosition.Bounds);
  }
end;

Procedure TCustomBarSeries.SetUseOrigin(Value:Boolean);
Begin
  SetBooleanProperty(FUseOrigin,Value);
End;

procedure TCustomBarSeries.SetShadow(Const Value:TTeeShadow);
begin
  FShadow.Assign(Value);
end;

procedure TCustomBarSeries.SetEmboss(Const Value:TTeeEmboss);
begin
  FEmboss.Assign(Value);
end;

procedure TCustomBarSeries.SetSelfStack(const Value:TSelfStack);
begin
  FSelfStack.Assign(Value);
end;

Procedure TCustomBarSeries.SetSideMargins(Value:Boolean);
Begin
  SetBooleanProperty(FSideMargins,Value);
  SetOtherBars(True);
end;

Procedure TCustomBarSeries.SetGradientRelative(const Value:Boolean);
begin
  SetBooleanProperty(FGradientRelative,Value);
end;

Procedure TCustomBarSeries.SetDark3D(Value:Boolean);
Begin
  SetBooleanProperty(FDark3D,Value);
End;

Procedure TCustomBarSeries.SetDarkPen(Value:Integer);
begin
  SetIntegerProperty(FDarkPen,Value);
end;

Function TCustomBarSeries.InternalCalcMarkLength(ValueIndex:Integer):Integer; // virtual; abstract;
begin
  result:=0;
end;

Function TCustomBarSeries.InternalClicked(ValueIndex:Integer; P:TPoint):Boolean; // virtual; abstract;
begin
  result:=False;
end;

Procedure TCustomBarSeries.PrepareForGallery;
Begin
  inherited;

  BarWidthPercent:=85;
  BarStyle:=bsRectGradient;
  Gradient.Visible:=True;
  MultiBar:=mbNone;
  DarkPen:=130;
end;

Procedure TCustomBarSeries.SetOrigin(Const Value:Double);
Begin
  SetDoubleProperty(FOrigin,Value);
End;

Procedure TCustomBarSeries.SetMarksOnBar(Value:Boolean);
Begin
  SetBooleanProperty(FMarksOnBar,Value);
end;

procedure TCustomBarSeries.SetMarksLocation(const Value:TMarksLocation);
begin
  if FMarksLocation<>Value then
  begin
    FMarksLocation:=Value;
    Repaint;
  end;
end;

Function TCustomBarSeries.PointInDiamond(const P:TPoint; X1,Y1,X2,Y2:Integer):Boolean;
var tmp : TFourPoints;
    tmpMidX,
    tmpMidY : Integer;
begin
  tmpMidX:=(X1+X2) div 2;
  tmpMidY:=(Y1+Y2) div 2;

  tmp[0]:=TeePoint(tmpMidX,Y1);
  tmp[1]:=TeePoint(X2,tmpMidY);
  tmp[2]:=TeePoint(tmpMidX,Y2);
  tmp[3]:=TeePoint(X1,tmpMidY);

  if ParentChart.View3D then
  with ParentChart.Canvas do
  begin
    tmp[0]:=Calculate3DPosition(tmp[0],StartZ);
    tmp[1]:=Calculate3DPosition(tmp[1],StartZ);
    tmp[2]:=Calculate3DPosition(tmp[2],StartZ);
    tmp[3]:=Calculate3DPosition(tmp[3],StartZ);
  end;

  result:=PointInPolygon(P,tmp);
end;

function TCustomBarSeries.PointInCube(const P:TPoint; const R:TRect; Z1,Z2:Integer):Boolean;
var tmp : TPointArray;
begin
  {$IFNDEF D7}
  tmp:=nil; // <-- Fix: Hint
  {$ENDIF}

  if ParentChart.View3D then
  begin
    tmp:=ParentChart.Canvas.CubeHull(R,Z1,Z2);
    result:=PointInPolygon(P,tmp);
    tmp:=nil;
  end
  else
     result:=PointInRect(R,P);
end;

Function TCustomBarSeries.Clicked(x,y:Integer):Integer;
var t : Integer;
    P : TPoint;
Begin
  if (FirstValueIndex>-1) and (LastValueIndex>-1) then
  begin
    P.X:=X;
    P.Y:=Y;

    for t:=FirstValueIndex to Min(LastValueIndex,Count-1) do
    if InternalClicked(t,P) then
    begin
      result:=t;
      exit;
    end;
  end;

  result:=TeeNoPointClicked;
end;

Procedure TCustomBarSeries.SetOtherBars(SetOthers:Boolean);
var t : Integer;
Begin
  if Assigned(ParentChart) then
  with ParentChart do
  for t:=0 to SeriesCount-1 do
    if Series[t] is Self.ClassType then
       with TCustomBarSeries(Series[t]) do
       begin
         if SetOthers then
         begin
           FMultiBar:=Self.FMultiBar;
           FSideMargins:=Self.FSideMargins;
         end
         else
         begin
           Self.FMultiBar:=FMultiBar;
           Self.FSideMargins:=FSideMargins;
           break;
         end;

         CalcVisiblePoints:=FMultiBar<>mbSelfStack;
       end;
end;

procedure TCustomBarSeries.SetRoundSize(Value:Integer);
begin
  SetIntegerProperty(FRoundSize,Value);
end;

Procedure TCustomBarSeries.SetMultiBar(Value:TMultiBar);
Begin
  if Value<>FMultiBar then
  Begin
    FMultiBar:=Value;
    SetOtherBars(True);
    Repaint;
  end;
End;

Function TCustomBarSeries.InternalGetOriginPos(ValueIndex:Integer; DefaultOrigin:Integer):Integer;
var tmp      : Double;
    tmpValue : Double;
Begin
  tmpValue:=PointOrigin(ValueIndex,False);

  Case FMultiBar of
    mbStacked,
    mbSelfStack: result:=CalcPosValue(tmpValue);
    mbStacked100:
      begin
        tmp:=PointOrigin(ValueIndex,True);
        if tmp<>0 then result:=CalcPosValue(tmpValue*100.0/tmp)
                  else result:=0;
      end;
  else
    if FUseOrigin then result:=CalcPosValue(tmpValue)
                  else result:=DefaultOrigin;
  end;
end;

function TCustomBarSeries.IsPointInChartRect(ValueIndex: Integer): Boolean;
begin
  result:=TeeRectIntersects(ParentChart.ChartRect,CalcBarBounds(ValueIndex));
end;

Function TCustomBarSeries.PointOrigin(ValueIndex:Integer; SumAll:Boolean):Double;

  Function InternalPointOrigin:Double;
  var t         : Integer;
      tmpSeries : TChartSeries;
      tmp       : Double;
      tmpValue  : Double;
  Begin
    result:=0;

    if Assigned(ParentChart) then
    with ParentChart do
    begin
      tmpValue:=MandatoryValueList.Value[ValueIndex];

      for t:=0 to SeriesCount-1 do
      Begin
        tmpSeries:=Series[t];

        if (not SumAll) and (tmpSeries=Self) then Break
        else
        With tmpSeries do
        if Active and SameClass(Self) and (Count>ValueIndex)
           and (TCustomBarSeries(tmpSeries).StackGroup=Self.StackGroup) then
        begin
          tmp:=GetOriginValue(ValueIndex);

          //TV52014908
          if SumAll then
          begin
            if tmp<0 then
               result:=result+(-tmp)
            else
               result:=result+tmp;
          end
          else
          begin
            if tmpValue<0 then
            begin
              if tmp<0 then result:=result+tmp;
            end
            else
              if tmp>0 then result:=result+tmp;
          end;
        end;
      end;
    end;
  end;

var t : Integer;
    tmpPositive : Boolean;
begin
  if (FMultiBar=mbStacked) or
     (FMultiBar=mbStacked100) then
    result:=InternalPointOrigin
  else
  if FMultiBar=mbSelfStack then
  begin
    result:=0;

    tmpPositive:=MandatoryValueList.Value[ValueIndex]>=YOrigin;

    for t:=0 to ValueIndex-1 do
        if (tmpPositive and (MandatoryValueList.Value[t]>=YOrigin)) or
           ((not tmpPositive) and (MandatoryValueList.Value[t]<YOrigin)) then
               result:=result+MandatoryValueList.Value[t];
  end
  else
    result:=FOrigin;
End;

function TCustomBarSeries.DrawSeriesAscending:Boolean;

  function CheckRotation:Boolean;
  begin
    with ParentChart.View3DOptions do
      result:=((Rotation>0) and (Rotation<90)) or
              ((Rotation>180) and (Rotation<270));
  end;

begin
  result:=inherited DrawSeriesAscending;

  if (FMultiBar=mbSideAll) or (FMultiBar=mbSelfStack) then
  begin
    if NotMandatoryAxis.Inverted then
       result:=not result;

    if ParentChart.DepthAxis.Inverted then
       result:=not result;

    if CheckRotation then
       result:=not result;
  end;
end;

Procedure TCustomBarSeries.DrawTickLine(TickPos:Integer; AStyle:TBarStyle);
begin
end;

type
  TBrushAccess=class(TTeeBrush);

Procedure TCustomBarSeries.DrawLegendShape(ValueIndex:Integer; Const Rect:TRect);
begin
  // Avoid referencing Image property directly (use HasImage) to avoid creating TPicture
  if TBrushAccess(BarBrush).HasImage and BarBrush.Image.Valid then
     ParentChart.Canvas.Brush.Bitmap{$IFDEF FMX}.Bitmap{$ENDIF}:=BarBrush.Image.Bitmap;

  inherited;
end;

Procedure TCustomBarSeries.PrepareBarPen(ValueIndex:Integer);
begin
  if not PrepareHoverPen(ValueIndex) then
     if (FDarkPen>0) and (ValueIndex<>-1) then
        ParentChart.Canvas.AssignVisiblePenColor(BarPen,ApplyDark(ValueColor[ValueIndex],255-FDarkPen))
     else
        ParentChart.Canvas.AssignVisiblePen(BarPen);
end;

Procedure TCustomBarSeries.PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                                var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
Begin
  PrepareBarPen(ValueIndex);

  BrushStyle:=BarBrush.Style;

  if BarBrush.Color=clTeeColor then BackColor:=ParentChart.Color
                               else BackColor:=BarBrush.Color;
end;

procedure TCustomBarSeries.SetPen(const Value: TChartPen);
begin
  inherited; 
end;

Procedure TCustomBarSeries.SetPenBrushBar(BarColor:TColor; ValueIndex:Integer=-1);
var tmpBack : TColor;
Begin
  PrepareBarPen(ValueIndex);

  with ParentChart do
  begin
    if BarBrush.Color=clTeeColor then tmpBack:=Color
                                 else tmpBack:=BarBrush.Color;

    SetBrushCanvas(BarColor,BarBrush,tmpBack);
  end;
end;

Procedure TCustomBarSeries.BarRectangle( BarColor:TColor;
                                         ALeft,ATop,ARight,ABottom:Integer;
                                         Rounded:Boolean=False);
var tmpR3D     : TRect;
    tmpCorners : TTeeCorners;
begin
  With ParentChart.Canvas do
  begin
    if Brush.Style=bsSolid then
    Begin
      Pen.Width:=Self.Pen.Width;

      if (ARight=ALeft) or (ATop=ABottom) then
      Begin
        Pen.Color:=Brush.Color;
        if Pen.Style=psClear then Pen.Style:=psSolid;

        Line(ALeft,ATop,ARight,ABottom);
        Exit;
      end
      else
      if Self.Pen.Visible and ((Abs(ARight-ALeft)<=Pen.Width) or (Abs(ABottom-ATop)<=Pen.Width)) then
      Begin
        Pen.Color:=Brush.Color;
        if Pen.Style=psClear then Pen.Style:=psSolid;

        Brush.Style:=bsClear;
      end;
    end;

    if Rounded then
    begin
      tmpR3D:=TeeRect(ALeft,ATop,ARight,ABottom);

      if ATop>ABottom then
         {$IFDEF FMX}SwapSingle{$ELSE}SwapInteger{$ENDIF}(tmpR3D.Top,tmpR3D.Bottom);

      if ALeft>ARight then
         {$IFDEF FMX}SwapSingle{$ELSE}SwapInteger{$ENDIF}(tmpR3D.Left,tmpR3D.Right);

      if BarRound=brBothEnds then
         RoundRect(tmpR3D,RoundSize,RoundSize)
      else
      if BarRound=brAtValue then
      begin
        tmpCorners:=TTeeCorners.Create;

        if YMandatory then
        begin
          if ATop>ABottom then
          begin
            tmpCorners.LeftTop.Style:=cosNone;
            tmpCorners.RightTop.Style:=cosNone;
          end
          else
          begin
            tmpCorners.LeftBottom.Style:=cosNone;
            tmpCorners.RightBottom.Style:=cosNone;
          end;
        end
        else
        begin
          if ALeft>ARight then
          begin
            tmpCorners.RightTop.Style:=cosNone;
            tmpCorners.RightBottom.Style:=cosNone;
          end
          else
          begin
            tmpCorners.LeftTop.Style:=cosNone;
            tmpCorners.LeftBottom.Style:=cosNone;
          end;
        end;

        RoundRect(tmpR3D,RoundSize,RoundSize,tmpCorners);

        tmpCorners.Free;
      end
      else
        Rectangle(ALeft,ATop,ARight,ABottom);
    end
    else
      Rectangle(ALeft,ATop,ARight,ABottom);
  end;
end;

Procedure TCustomBarSeries.BarRectangle(BarColor:TColor; const R:TRect; Rounded:Boolean=False);
begin
  BarRectangle(BarColor, {$IFDEF FMX}Round{$ENDIF}(R.Left),
                         {$IFDEF FMX}Round{$ENDIF}(R.Top),
                         {$IFDEF FMX}Round{$ENDIF}(R.Right),
                         {$IFDEF FMX}Round{$ENDIF}(R.Bottom), Rounded);
End;

Function TCustomBarSeries.GetBarStyle(ValueIndex:Integer):TBarStyle;
Begin
  result:=FBarStyle;

  if Assigned(FOnGetBarStyle) then
     FOnGetBarStyle(Self,ValueIndex,result);
end;

function TCustomBarSeries.GetAutoMarkPosition:Boolean;
begin
  result:=Marks.Automatic.Move;
end;

Function TCustomBarSeries.GetBarBrush:TChartBrush;
begin
  result:=Brush;
end;

Function TCustomBarSeries.GetBarPen:TChartPen;
begin
  result:=Pen;
end;

function TCustomBarSeries.GetGradient:TTeeGradient;
begin
  result:=Brush.Gradient;
end;

Procedure TCustomBarSeries.SetGradient(const Value:TTeeGradient);
begin
  Brush.Gradient.Assign(Value);
end;

class Function TCustomBarSeries.GetEditorClass:String;
Begin
  result:='TBarSeriesEditor'; { <-- dont translate ! }
end;

Function TCustomBarSeries.BarMargin:Integer;
Begin
  result:=IBarSize;

  if (FMultiBar<>mbSideAll) and (FMultiBar<>mbSelfStack) then
     result:=result*INumBars;

  if not SideMargins then
     result:=result div 2;
end;

Function TCustomBarSeries.ApplyBarOffset(Position:Integer):Integer;
Begin
  result:=Position;

  if OffsetPercent<>0 then
     Inc(result,Round(OffsetPercent*IBarSize*0.01));
end;

Procedure TCustomBarSeries.SetAutoMarkPosition(Value:Boolean);
Begin
  Marks.Automatic.Move:=Value;
end;

Procedure TCustomBarSeries.SetAutoBarSize(Value:Boolean);
Begin
  SetBooleanProperty(FAutoBarSize,Value);
end;

Procedure TCustomBarSeries.Assign(Source:TPersistent);
begin
  if Source is TCustomBarSeries then
  with TCustomBarSeries(Source) do
  begin
    Self.FBarStyle   :=FBarStyle;
    Self.FMultiBar   :=FMultiBar;
    Self.FOrigin     :=FOrigin;
    Self.SelfStack   :=FSelfStack; // <-- Force call to setter
    Self.FStackGroup :=FStackGroup;
    Self.FUseOrigin  :=FUseOrigin;
  end;

  inherited;
end;

Procedure TCustomBarSeries.AssignFormatting(Series:TChartSeries);
begin
  if Series is TCustomBarSeries then
  With TCustomBarSeries(Series) do
  begin
    Self.FBarWidthPercent:=FBarWidthPercent;
    Self.FBarRound       :=FBarRound;
    Self.FBevelSize      :=FBevelSize;
    Self.FConePercent    :=FConePercent;
    Self.FCustomBarSize  :=FCustomBarSize;
    Self.FDark3D         :=FDark3D;
    Self.FDarkPen        :=FDarkPen;
    Self.FDepthPercent   :=FDepthPercent;
    Self.Emboss          :=Emboss;
    Self.FGradientRelative:=FGradientRelative;
    Self.FMarksLocation  :=FMarksLocation;
    Self.FMarksOnBar     :=FMarksOnBar;
    Self.FOffsetPercent  :=FOffsetPercent;
    Self.FRoundSize      :=FRoundSize;
    Self.Shadow          :=Shadow;
    Self.FSideMargins    :=FSideMargins;
    Self.TickLines       :=FTickLines;
  end;

  inherited;
end;

Function TCustomBarSeries.AddBar( Const AValue:Double;
                                  Const ALabel:String; AColor:TColor):Integer;
begin
  result:=Add(AValue,ALabel,AColor);
end;

procedure TCustomBarSeries.SwapGradientColors;
var tmpColor : TColor;
begin
  with Gradient do
  begin
    tmpColor:=StartColor;
    StartColor:=EndColor;
    EndColor:=tmpColor;
  end;
end;

function TCustomBarSeries.CalcBarGradient(ValueIndex:Integer):Boolean;

  Procedure CalcGradientColor(ValueIndex:Integer);
  var tmpRatio : Double;
      T0,T1,T2 : Byte;
      RGB      : TRGB;
  begin
    if GradientRelative then
    begin
      with MandatoryValueList do
      begin
        if UseYOrigin then
           if Value[ValueIndex]>=YOrigin then
              tmpRatio:=(Value[ValueIndex]-YOrigin)/(MaxValue-YOrigin)
           else
              tmpRatio:=(YOrigin-Value[ValueIndex])/(YOrigin-MinValue)
        else
           tmpRatio:=1-((MaxValue-Value[ValueIndex])/Range);
      end;

      RGB:=RGBValue(NormalBarColor);

      T0:=Byte(Gradient.StartColor);
      T1:=Byte(Gradient.StartColor shr 8);
      T2:=Byte(Gradient.StartColor shr 16);

      Gradient.EndColor:=
           (( T0 + Round(tmpRatio*(RGB.Red-T0))) or
           (( T1 + Round(tmpRatio*(RGB.Green-T1)) ) shl 8) or
           (( T2 + Round(tmpRatio*(RGB.Blue-T2)) ) shl 16));
    end
    else
      Gradient.EndColor:=NormalBarColor;
  end;

begin
  CalcGradientColor(ValueIndex);

  result:=UseYOrigin and GradientRelative and {(not Gradient.FromMinimum) and}
           (MandatoryValueList.Value[ValueIndex]<YOrigin);

  if MandatoryAxis.Inverted then
     result:=not result;

  if result then
     SwapGradientColors;
end;

Function TCustomBarSeries.MaxMandatoryValue(Const Value:Double):Double;
var t   : Integer;
    tmp : Double;
begin
  if FMultiBar=mbStacked100 then
  begin
    if MandatoryValueList.Total < 0 then result:=0 else result:=100;
  end
  else
  begin
    result:=Value;

    if (FMultiBar=mbStacked) or (FMultiBar=mbSelfStack) then
    for t:=0 to Count-1 do
    Begin
      tmp:=PointOrigin(t,False)+MandatoryValueList.Value[t];
      if tmp>result then result:=tmp;
    end;

    if FUseOrigin and (result<FOrigin) then result:=FOrigin;
  end;
end;

Function TCustomBarSeries.MinMandatoryValue(Const Value:Double):Double;
var t   : Integer;
    tmp : Double;
begin
  //TV52014908
  if FMultiBar=mbStacked100 then
  begin
    result:=0;

    for t:=0 to Count-1 do
    begin
      tmp:=PointOrigin(t,True);

      if tmp<>0 then
         result:=Math.Min(result,(PointOrigin(t,False)+MandatoryValueList.Value[t])*100/tmp)
      else
         result:=Math.Min(result, MandatoryAxis.IEndPos);
    end;
  end
  else
  begin
    result:=Value;

    if (FMultiBar=mbStacked) or (FMultiBar=mbSelfStack) then
    for t:=0 to Count-1 do
    Begin
      tmp:=PointOrigin(t,False)+MandatoryValueList.Value[t];
      if tmp<result then result:=tmp;
    end;

    if FUseOrigin and (result>FOrigin) then result:=FOrigin;
  end;
end;

function TCustomBarSeries.NotMandatoryAxis:TChartAxis;
begin
  if YMandatory then result:=GetHorizAxis
                else result:=GetVertAxis;
end;

Procedure TCustomBarSeries.CalcBarWidth;
var tmpRange : Double;
//    tmpZoom : Double;
begin
  if FCustomBarSize<>0 then
     IBarSize:=FCustomBarSize
  else
  if IMaxBarPoints>0 then
  begin
    with NotMandatoryAxis do
    if FAutoBarSize then
       tmpRange:=IMaxBarPoints*IAxisSize/(2.0+Maximum-Minimum)
    else
       // TV52014436
       tmpRange:=IAxisSize;

    tmpRange:=(FBarWidthPercent*0.01)*tmpRange/IMaxBarPoints;

    if FMultiBar=mbSelfStack then
       IBarSize:=Round(tmpRange*0.8)
    else
       IBarSize:=Round(tmpRange/INumBars);

    if (IBarSize mod 2)=1 then
       Inc(IBarSize);
  end
  else IBarSize:=0;
end;

Procedure TCustomBarSeries.InternalApplyBarMargin(var MarginA,MarginB:Integer);
var tmp : Integer;
begin
  CalcBarWidth;
  tmp:=BarMargin;
  Inc(MarginA,tmp);
  Inc(MarginB,tmp);
end;

procedure TCustomBarSeries.DrawValue(ValueIndex:Integer);

  procedure DoDrawBar(Transp:Integer);
  var tmpBlend : TTeeBlend;
  begin
    if Transp>0 then
       tmpBlend:=CreateBlend(Transp)
    else
       tmpBlend:=nil;

    with FBarBounds{$IFDEF FMX}.Round{$ENDIF} do
    if YMandatory then
       if Bottom>Top then DrawBar(ValueIndex, Top, Bottom)
                     else DrawBar(ValueIndex, Bottom, Top)
    else
       if Right>Left then DrawBar(ValueIndex, Left, Right)
                     else DrawBar(ValueIndex, Right, Left);

    if Transp>0 then
       ParentChart.Canvas.EndBlending(tmpBlend);
  end;

  procedure TryDrawShadow(const AShadow:TTeeShadow);

    function CalcRoundSize:Integer;
    begin
      if BarStyle=bsRoundRectangle then
         result:=RoundSize
      else
         result:=0;
    end;

    procedure GetShadowSize(out W,H:Integer);
    begin
      W:=Shadow.HorizSize;
      H:=Shadow.VertSize;

      case Shadow.Direction of
        sdLeftTop    : begin W:=-W; H:=-H; end;
        sdLeftBottom : W:=-W;
        sdRightTop   : H:=-H;
      end;
    end;

    procedure CalcShadowBounds(const AShadow:TTeeShadow);
    var tmpW,
        tmpH : Integer;
    begin
      GetShadowSize(tmpW,tmpH);

      with FBarBounds do
      if YMandatory then
      begin
        Left:=Left+tmpW;
        Right:=Right+tmpW;

        if Bottom>Top then
           Top:=Top+tmpH
        else
           Bottom:=Bottom-tmpH;
      end
      else
      begin
        Top:=Top+tmpH;
        Bottom:=Bottom+tmpH;

        if Right>Left then
           Right:=Right+tmpW
        else
           Left:=Left-tmpW;
      end;
    end;

  var Old     : TColor;
      OldRect : TRect;
      tmpP    : TFourPoints;
  begin
    if AShadow.ShouldDraw then
    begin
      Old:=BarPen.Color;
      NormalBarColor:=AShadow.Color;
      BarPen.Color:=NormalBarColor;

      OldRect:=FBarBounds;
      CalcShadowBounds(AShadow);

      if AShadow.Smooth and
         ( (BarStyle=bsRectangle) or
           (BarStyle=bsRectGradient) or
           ((BarStyle=bsRoundRectangle) and (FBarRound=brBothEnds)) or
           (BarStyle=bsBevel) ) then

         if ParentChart.View3D then
            if ParentChart.View3DOptions.Orthogonal then
            begin
              if ParentChart.Canvas.SupportsFullRotation then
                 AShadow.ZPosition:=EndZ;

              AShadow.Draw(ParentChart.Canvas,OldRect,CalcRoundSize)
            end
            else
            begin
              ParentChart.Canvas.FourPointsFromRect(OldRect,EndZ,tmpP);
              AShadow.Draw(ParentChart.Canvas,tmpP);
            end
         else
            AShadow.Draw(ParentChart.Canvas,OldRect,CalcRoundSize)
      else
         DoDrawBar(AShadow.Transparency);

      FBarBounds:=OldRect;

      NormalBarColor:=ValueColor[ValueIndex];
      BarPen.Color:=Old;
    end;
  end;

  procedure CreateVisual;
  begin
    TryDrawShadow(Shadow);
    TryDrawShadow(Emboss);

    DoDrawBar(Transparency);
  end;

var tmp : TVisualBlock;
begin
  inherited;

  NormalBarColor:=ValueColor[ValueIndex];

  if NormalBarColor<>clNone then { <-- if value is not null }
  begin
    FBarBounds:=CalcBarBounds(ValueIndex);

    tmp:=Visuals[ValueIndex];

    if Assigned(tmp) then
    begin
      TVisualAccess(tmp).SetBounds(FBarBounds,StartZ,EndZ,ParentChart.Height);
      TVisualAccess(tmp).SetFormatting(BarPen,BarBrush,False,NormalBarColor,Transparency);
    end
    else
    if ((MandatoryValueList.Value[valueIndex]<>0) or ((MultiBar<>mbStacked) and (MultiBar<>mbStacked100) and (MultiBar<>mbSelfStack))) then // TV52014153
       CreateVisual;
  end;
end;

function CreateNewSubSeries(const ASeries:TChartSeries):TChartSeries;
begin
  result:=ASeries.ParentChart.AddSeries(TChartSeriesClass(ASeries.ClassType));
  result.Name:=GetNewSeriesName(ASeries.ParentChart.Owner);
  result.Title:=''; { <-- force broadcast event to ChartListBox and others }
end;

class Procedure TCustomBarSeries.SetSubGallery(ASeries:TChartSeries; Index:Integer);

  function PrepareSeries(const ASeries:TChartSeries):TChartSeries;
  begin
    ASeries.FillSampleValues(2);
    ASeries.ColorEachPoint:=False;
    ASeries.Marks.Hide;

    result:=ASeries;
  end;

var tmp : TChartSeries;
begin
  With TCustomBarSeries(ASeries) do
  Case Index of
     0: ;
     1: ColorEachPoint:=True;
     2: BarStyle:=bsPyramid;
     3: BarStyle:=bsEllipse;
     4: BarStyle:=bsInvPyramid;
  else
  begin
    if Assigned(ParentChart) and (ParentChart.SeriesCount=1) then
    begin
      PrepareSeries(ASeries);
      tmp:=PrepareSeries(CreateNewSubSeries(ASeries));

      (tmp as TCustomBarSeries).Gradient.Visible:=True;
      (tmp as TCustomBarSeries).BarWidthPercent:=BarWidthPercent;

      SetSubGallery(tmp,Index);
    end;

    if not SubGalleryStack then
       Inc(Index,3);

    case Index of
       5: MultiBar:=mbStacked;
       6: MultiBar:=mbStacked100;
       7: MultiBar:=mbSelfStack;
       8: MultiBar:=mbSide;
       9: MultiBar:=mbSideAll;
    else
       inherited;
    end;
  end;
  end;
end;

class Procedure TCustomBarSeries.CreateSubGallery(AddSubChart:TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Colors);
  AddSubChart(TeeMsg_Pyramid);
  AddSubChart(TeeMsg_Ellipse);
  AddSubChart(TeeMsg_InvPyramid);

  if SubGalleryStack then
  begin
    AddSubChart(TeeMsg_Stack);
    AddSubChart(TeeMsg_Stack100);
    AddSubChart(TeeMsg_SelfStack);
  end;

  AddSubChart(TeeMsg_Sides);
  AddSubChart(TeeMsg_SideAll);
end;

procedure TCustomBarSeries.SetStackGroup(Value: Integer);
begin
  SetIntegerProperty(FStackGroup,Value);
end;

procedure TCustomBarSeries.SetConePercent(const Value: Integer);
begin
  SetIntegerProperty(FConePercent,Value);
end;

procedure TCustomBarSeries.SetSides(const Value: Integer);
begin
  SetIntegerProperty(FSides,Value);
end;

class function TCustomBarSeries.SubGalleryStack: Boolean;
begin
  result:=True;
end;

function TCustomBarSeries.CreateBlend(Transp:Integer): TTeeBlend;
var R  : TRect;
    R1 : TRect;
    R2 : TRect;
    tmpR : TRect;
begin
  if ParentChart.Canvas.SupportsAlpha then
     result:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transp)
  else
  begin
    if ParentChart.View3D then
    begin
      tmpR:=FBarBounds;

      if BarPen.Visible then
      begin
        {$IFDEF TEENORECTOFFSET}
        tmpR.Right:=tmpR.Right+BarPen.Width;
        tmpR.Bottom:=tmpR.Bottom+BarPen.Width;
        {$ELSE}
        Offset(tmpR.BottomRight,BarPen.Width);
        {$ENDIF}
      end;

      R1:=OrientRectangle(ParentChart.Canvas.CalcRect3D(tmpR,StartZ));
      R2:=OrientRectangle(ParentChart.Canvas.CalcRect3D(tmpR,EndZ));

      {$IFDEF FMX}
      R:=TRect.Union(R1,R2);
      {$ELSE}
      UnionRect(R,R1,R2);
      {$ENDIF}
    end
    else
      R:=FBarBounds;

    result:=ParentChart.Canvas.BeginBlending(R,Transp);
  end;
end;

procedure TCustomBarSeries.DrawBevel;
var tmpR : TRect;
begin
  if ParentChart.View3D and (not ParentChart.View3DOptions.Orthogonal) then
     exit;

  tmpR:=BarBounds;

  if BarPen.Visible then
  begin
    {$IFDEF D16}System.Types.{$ENDIF}InflateRect(tmpR,-BarPen.Width,-BarPen.Width);

    if ParentChart.View3D and ParentChart.View3DOptions.Orthogonal then
    begin
      {$IFDEF TEENORECTOFFSET}
      tmpR.Right:=tmpR.Right+1;
      tmpR.Bottom:=tmpR.Bottom+1;
      {$ELSE}
      Offset(tmpR.BottomRight,1);
      {$ENDIF}
    end;
  end;

  ParentChart.Canvas.Frame3D(tmpR,ApplyBright(NormalBarColor,DarkerColorQuantity),
                             ApplyDark(NormalBarColor,DarkColorQuantity),BevelSize);
end;

procedure TCustomBarSeries.SetBevelSize(const Value: Integer);
begin
  SetIntegerProperty(FBevelSize,Value);
end;

type
  TTeePenAccess=class(TTeePen);

function TCustomBarSeries.GetTickLines:TChartHiddenPen;
begin
  if not Assigned(FTickLines) then
  begin
    // Tick line with flat ending style (to avoid overriding border)
    FTickLines:=TChartHiddenPen.Create(CanvasChanged);
    TTeePenAccess(FTickLines).InitEndStyle(esFlat);
  end;

  result:=FTickLines;
end;

procedure TCustomBarSeries.SetTickLines(const Value: TChartHiddenPen);
begin
  if Assigned(Value) then
     TickLines.Assign(Value)
  else
     FreeAndNil(FTickLines);
end;

procedure TCustomBarSeries.DrawCylinderTicks(TickPos:Integer);
var t : Integer;

    tmpPos,
    tmpStep,
    tmpRad,
    tmpZRad,
    tmpMid,
    tmpMidZ : Single;
    P : TPointFloatArray;

    tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
begin
  SetLength(P,TeeNumCylinderSides);

  if YMandatory then
  begin
    tmpRad:=(BarBounds.Right-BarBounds.Left)*0.5;
    tmpMid:=(BarBounds.Right+BarBounds.Left)*0.5;
  end
  else
  begin
    tmpRad:=(BarBounds.Bottom-BarBounds.Top)*0.5;
    tmpMid:=(BarBounds.Bottom+BarBounds.Top)*0.5;
  end;

  tmpMidZ:=(StartZ+EndZ)*0.5;
  tmpZRad:=(EndZ-StartZ)*0.5;

  tmpStep:=Pi/TeeNumCylinderSides;

  for t:=0 to TeeNumCylinderSides-1 do
  begin
    SinCos(Pi+t*tmpStep,tmpSin,tmpCos);
    tmpPos:=tmpMid-tmpCos*tmpRad;

    if YMandatory then
    begin
      P[t].X:=tmpPos;
      P[t].Y:=TickPos;
    end
    else
    begin
      P[t].Y:=tmpPos;
      P[t].X:=TickPos;
    end;

    {$IFDEF FMX}
    P[t]:=ParentChart.Canvas.Calculate3DPosition(P[t].X,P[t].Y,tmpMidZ+tmpSin*tmpZRad);
    {$ELSE}
    ParentChart.Canvas.Calculate3DPosition(P[t],tmpMidZ+tmpSin*tmpZRad);
    {$ENDIF}
  end;

  {$IFDEF FMX}
  ParentChart.Canvas.Polyline(P);
  {$ELSE}
  ParentChart.Canvas.PolylineFloat(P);
  {$ENDIF}

  P:=nil;
end;

procedure TCustomBarSeries.DrawTickLines(StartPos,EndPos:Integer; AStyle:TBarStyle);
var t : Integer;
begin
  if Assigned(FTickLines) and FTickLines.Visible then
  begin
    ParentChart.Canvas.AssignVisiblePen(FTickLines);

    with MandatoryAxis do
    for t:=0 to Length(Tick)-1 do
        if (Tick[t]>StartPos) and (Tick[t]<EndPos) then
           DrawTickLine(Tick[t],AStyle);
  end;
end;

procedure TCustomBarSeries.AddSampleValues(NumValues: Integer; Sequential:Boolean=False);
var t : Integer;
begin
  if Assigned(ParentChart) then
  with ParentChart do
  for t:=0 to SeriesCount-1 do
  if (Series[t]<>Self) and SameClass(Series[t]) and HasNoMandatoryValues(Series[t]) then
  begin
    inherited;
    break;
  end;

  inherited AddSampleValues(NumValues,True);
end;

{ TBarSeries }
Constructor TBarSeries.Create(AOwner:TComponent);
begin
  inherited;
  Gradient.Direction:=gdTopBottom;
  MandatoryValueList.Name:=TeeMsg_ValuesBar;
end;

procedure TBarSeries.CalcSelectionPos(ValueIndex:Integer; out X,Y:Integer);
begin
  inherited;
  Inc(X,(IBarSize shr 1));
end;

{ The horizontal Bar position is the "real" X pos +
  the BarWidth by our BarSeries order }
Function TBarSeries.CalcXPos(ValueIndex:Integer):Integer;
Begin
  if FMultiBar=mbSideAll then
     result:=GetHorizAxis.CalcXPosValue(IPreviousCount+ValueIndex)-(IBarSize shr 1)
  else
  if FMultiBar=mbSelfStack then
     result:=(inherited CalcXPosValue(MinXValue))-(IBarSize shr 1)
  else
  begin
    result:=inherited CalcXPos(ValueIndex);

    if FMultiBar<>mbNone then
       result:=result+Round(IBarSize*((IOrderPos-(INumBars*0.5))-1.0))
    else
       result:=result-(IBarSize shr 1);
  end;

  result:=ApplyBarOffset(result);
End;

Function TBarSeries.MaxXValue:TChartValue;
Begin
  case FMultiBar of
    mbSideAll  : result:=IPreviousCount+Count-1;
    mbSelfStack: result:=MinXValue;
  else result:=inherited MaxXValue;
  end;
end;

Function TBarSeries.MinXValue:TChartValue;
Begin
  if MultiBar=mbSelfStack then
    if SelfStack.UsePosition then
       result:=SelfStack.Position
    else
       result:=SeriesIndex
  else
     result:=inherited MinXValue;
end;

Function TBarSeries.MaxYValue:TChartValue;
Begin
  result:=MaxMandatoryValue(inherited MaxYValue);
end;

Function TBarSeries.MinYValue:TChartValue;
Begin
  result:=MinMandatoryValue(inherited MinYValue);
end;

Procedure TBarSeries.CalcHorizMargins(var LeftMargin,RightMargin:Integer);
begin
  inherited;
  InternalApplyBarMargin(LeftMargin,RightMargin);
end;

Procedure TBarSeries.CalcVerticalMargins(var TopMargin,BottomMargin:Integer);
var tmp : Integer;
begin
  inherited;

  if FMarksOnBar then
     tmp:=0
  else
     tmp:=CalcMarkLength(0);

  if Pen.Visible then
     tmp:=Max(tmp,2+Round(Pen.Width*0.5));

  if tmp>0 then
  begin
    Inc(tmp);

    if FUseOrigin and (inherited MinYValue<FOrigin) then
       if GetVertAxis.Inverted then Inc(TopMargin,tmp) { 4.01 }
                               else Inc(BottomMargin,tmp);

    if (not FUseOrigin) or (inherited MaxYValue>FOrigin) then
       if GetVertAxis.Inverted then Inc(BottomMargin,tmp)
                               else Inc(TopMargin,tmp);
  end;
end;

Function TBarSeries.InternalCalcMarkLength(ValueIndex:Integer):Integer;
Begin
  result:=ParentChart.Canvas.FontHeight;
end;

type
  TSeriesMarksAccess=class(TSeriesMarks);

Procedure TBarSeries.DrawMark( ValueIndex:Integer; Const St:String;
                               APosition:TSeriesMarkPosition);
var DifW : Integer;
    DifH : Integer;
    tmpAngle : Integer;
    tmpBounds: TRect;
    tmpMargin : TCoordinate;
    tmpRotateSize : Integer;
Begin
  With APosition do
  begin
    DifW:=IBarSize div 2;
    DifH:=Marks.Callout.Length+Marks.Callout.Distance;

    if not MarksOnBar then
    begin
      if ArrowFrom.Y>GetOriginPos(ValueIndex) then
      begin
        DifH:=-DifH-Height;
        ArrowFrom.Y:=ArrowFrom.Y+Marks.Callout.Distance;
      end
      else
        ArrowFrom.Y:=ArrowFrom.Y-Marks.Callout.Distance;

      LeftTop.X:=LeftTop.X+DifW;
      LeftTop.Y:=LeftTop.Y-DifH;
      ArrowTo.X:=ArrowTo.X+DifW;
      ArrowTo.Y:=ArrowTo.Y-DifH;
      ArrowFrom.X:=ArrowFrom.X+DifW;
    end
    else
    begin
      TSeriesMarksAccess(Marks).Arrow.Visible:=False;
      LeftTop.X:=LeftTop.X+DifW;
      tmpBounds:=CalcBarBounds(ValueIndex);
      tmpAngle:=TSeriesMarksAccess(Marks).Angle;

      if MarksLocation=mlCenter then
      begin
        tmpMargin:=tmpBounds.Top + ((tmpBounds.Bottom-tmpBounds.Top) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});

        if (tmpAngle = 90) or (tmpAngle = 270) then
          LeftTop.Y := tmpMargin - APosition.Width {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}
        else
          LeftTop.Y := tmpMargin - APosition.Height {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
      end
      else
      begin
        if Marks.Transparent then
           Marks.Margins.Calculate(APosition.Width,APosition.Height,{$IFDEF FMX}Round{$ENDIF}(4*Marks.Font.Size));

        if tmpAngle=90 then
           tmpMargin:=Marks.Margins.Size.Left
        else
        if tmpAngle=270 then
           tmpMargin:=Marks.Margins.Size.Right
        else
           tmpMargin:=0;

        if (tmpAngle=0) or (tmpAngle=360) then
           tmpRotateSize:=APosition.Height div 2
        else
           tmpRotateSize:= {$IFDEF FMX}Round{$ENDIF}(tmpMargin + ((APosition.Width-APosition.Height){$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}));

        if MarksLocation=mlStart then
           if GetVertAxis.Inverted then
              LeftTop.Y:=tmpBounds.Bottom + tmpRotateSize
           else
              LeftTop.Y:=tmpBounds.Bottom - tmpRotateSize - APosition.Height
        else
        // mlEnd
           if GetVertAxis.Inverted then
              LeftTop.Y:=tmpBounds.Top - tmpRotateSize - APosition.Height
           else
              LeftTop.Y:=tmpBounds.Top + tmpRotateSize;
      end;
    end;
  end;

  inherited;
end;

{$IFDEF FMX}
type
  TRectInteger=record
    Left, Top, Right, Bottom : Integer;
  end;
{$ENDIF}

Procedure TBarSeries.DrawBar(BarIndex,StartPos,EndPos:Integer);
var tmp     : TBarStyle;
    OldSlant,
    OldEllipse,
    Old     : Integer;
    tmpSwap : Boolean;
    tmpSize : Integer;
    tmpMidX : Integer;

    {$IFDEF FMX}
    BoundsInt : TRectInteger;
    {$ENDIF}
begin
  if (not Assigned(ParentChart)) or (not Assigned(ParentChart.Canvas.Brush)) then
     Exit;

  tmp:=GetBarStyle(BarIndex);

  if tmp=bsRectGradient then
     Gradient.Visible:=True;

  if Gradient.Visible then
     tmpSwap:=CalcBarGradient(BarIndex)
  else
     tmpSwap:=False;

  SetPenBrushBar(NormalBarColor,BarIndex);

  if Pen.Visible then
  begin
    tmpSize:={$IFDEF FMX}Round(Pen.Width*0.5){$ELSE}(Pen.Width div 2){$ENDIF};

    if tmpSize>0 then
       InflateRect(FBarBounds,-tmpSize,-tmpSize);
  end;

  {$IFDEF FMX}
  BoundsInt.Left:=Round(BarBounds.Left);
  BoundsInt.Top:=Round(BarBounds.Top);
  BoundsInt.Right:=Round(BarBounds.Right);
  BoundsInt.Bottom:=Round(BarBounds.Bottom);
  {$ENDIF}

  with ParentChart,Canvas,{$IFDEF FMX}BoundsInt{$ELSE}BarBounds{$ENDIF} do
  begin
    tmpMidX:={$IFDEF FMX}Round{$ENDIF}(Left+Right) div 2;

    if View3D then
    begin
      Old:=TeeNumCylinderSides;
      OldEllipse:=NumCirclePoints;
      TeeNumCylinderSides:=Max(2,Self.Sides);
      NumCirclePoints:=2*TeeNumCylinderSides;

      Case tmp of
        bsRectangle,
        bsRectGradient,
        bsBevel      : Cube(Left,Right,StartPos,EndPos,StartZ,EndZ,FDark3D);
    bsRoundRectangle : Cube(Left,Right,StartPos,EndPos,StartZ,EndZ,FDark3D,RoundSize);
        bsPyramid    : Pyramid(True,Left,StartPos,Right,EndPos,StartZ,EndZ,FDark3D);
        bsInvPyramid : Pyramid(True,Left,EndPos,Right,StartPos,StartZ,EndZ,FDark3D);
        bsCylinder   : Cylinder(True,Left,Top,Right,Bottom,StartZ,EndZ,FDark3D);
        bsEllipse    : EllipseWithZ(Left,Top,Right,Bottom,MiddleZ);
        bsArrow      : Arrow(True, TeePoint(tmpMidX,EndPos),
                             TeePoint(tmpMidX,StartPos),
                             Right-Left,(Right-Left) div 2,StartZ,EndZ,50);
        bsInvArrow   : Arrow(True, TeePoint(tmpMidX,StartPos),
                             TeePoint(tmpMidX,EndPos),
                             Right-Left,(Right-Left) div 2,StartZ,EndZ,50);
        bsCone       : Cone(True,Left,StartPos,Right,EndPos,StartZ,EndZ,FDark3D,ConePercent);
        bsInvCone    : Cone(True,Left,EndPos,Right,StartPos,StartZ,EndZ,FDark3D,ConePercent);
        bsSlantCube  :
        begin
           OldSlant:=TeeNumCylinderSides;
           TeeNumCylinderSides:=4;
           Cylinder(True,Left,Top,Right,Bottom,StartZ,EndZ,FDark3D);
           TeeNumCylinderSides:=OldSlant;
        end;
        bsDiamond    : Diamond(Left,StartPos,Right,EndPos,StartZ,EndZ);
      end;

      TeeNumCylinderSides:=Old;
      NumCirclePoints:=OldEllipse;
    end
    else
      Case tmp of
        bsRectangle,
        bsRectGradient,
        bsBevel,
        bsCylinder   : BarRectangle(NormalBarColor,BarBounds);
    bsRoundRectangle : BarRectangle(NormalBarColor,BarBounds,True);
        bsPyramid,
        bsCone       : Polygon([ TeePoint(Left,EndPos),
                                 TeePoint(tmpMidX,StartPos),
                                 TeePoint(Right,EndPos) ]);
        bsInvPyramid,
        bsInvCone    : Polygon([ TeePoint(Left,StartPos),
                                 TeePoint(tmpMidX,EndPos),
                                 TeePoint(Right,StartPos) ]);
        bsEllipse    : Ellipse(BarBounds);
        bsArrow      : Arrow(True, TeePoint(tmpMidX,EndPos),
                                   TeePoint(tmpMidX,StartPos),
                                   Right-Left,(Right-Left) div 2,MiddleZ);
        bsInvArrow   : Arrow(True, TeePoint(tmpMidX,StartPos),
                                   TeePoint(tmpMidX,EndPos),
                                   Right-Left,(Right-Left) div 2,MiddleZ);
        bsSlantCube  : begin
                         BarRectangle(NormalBarColor,BarBounds);
                         DoVertLine(tmpMidX,Top,Bottom);
                       end;
        bsDiamond    : Diamond(Left,StartPos,Right,EndPos,StartZ,EndZ);
      end;

      if tmpSwap then
         SwapGradientColors;

      if tmp=bsBevel then
         DrawBevel;
  end;
  
  DrawTickLines(StartPos,EndPos,tmp);
end;

Function TBarSeries.DrawSeriesForward(ValueIndex:Integer):Boolean;
begin
  Case FMultiBar of
    mbNone,
    mbSide,
    mbSideAll: result:=inherited DrawSeriesForward(ValueIndex);

    mbSelfStack:
               begin
                 result:=YValues.Value[ValueIndex]>=YOrigin;
                 if GetVertAxis.Inverted then result:=not result;
               end;
  else
    result:=not MandatoryAxis.Inverted;
  end;
end;

Procedure TBarSeries.DrawTickLine(TickPos:Integer; AStyle:TBarStyle);
var tmpB,
    tmpT,
    tmp : TCoordinate;
    tmpBottom,
    P0,
    P1,
    P2 : TPoint;
    tmpZ : Integer;
begin
  case AStyle of
    bsRectangle,
    bsRectGradient,
    bsBevel,
    bsRoundRectangle:
    begin
      if ParentChart.View3D and ParentChart.View3DOptions.BackFaced then
         tmpZ:=EndZ
      else
         tmpZ:=StartZ;

      ParentChart.Canvas.HorizLine3D(
         {$IFDEF FMX}Round{$ENDIF}(BarBounds.Left),
         {$IFDEF FMX}Round{$ENDIF}(BarBounds.Right),
         TickPos,tmpZ);
    end;

    bsArrow:
    begin
      tmp:=(BarBounds.Right-BarBounds.Left) {$IFDEF FMX}*0.25{$ELSE}div 4{$ENDIF};
      ParentChart.Canvas.HorizLine3D(
        {$IFDEF FMX}Round{$ENDIF}(BarBounds.Left+tmp),
        {$IFDEF FMX}Round{$ENDIF}(BarBounds.Right-tmp),
        TickPos,MiddleZ);
    end;

    bsCylinder:
      if (not ParentChart.View3D) or ParentChart.View3DOptions.Orthogonal then
         DrawCylinderTicks(TickPos);
  end;

  if ParentChart.View3D then
  case AStyle of
    bsRectangle,
    bsRectGradient,
    bsBevel,
    bsRoundRectangle:
      begin
        tmpB:=BarBounds.Bottom;
        tmpT:=BarBounds.Top;

        if tmpT>tmpB then
           {$IFDEF FMX}
           SwapSingle(tmpB,tmpT);
           {$ELSE}
           SwapInteger(tmpB,tmpT);
           {$ENDIF}

        P0:=ParentChart.Canvas.Calculate3DPosition(TeePoint(BarBounds.Right,tmpB),StartZ);
        P1:=ParentChart.Canvas.Calculate3DPosition(TeePoint(BarBounds.Right,tmpB),EndZ);
        P2:=ParentChart.Canvas.Calculate3DPosition(TeePoint(BarBounds.Right,tmpT),EndZ);

        // Covers bar values above and below "YOrigin" (negative)
        if TeeCull(P0,P1,P2) then
        begin
          tmpBottom:=TeePoint(BarBounds.Left,tmpB);

          P0:=ParentChart.Canvas.Calculate3DPosition(tmpBottom,StartZ);
          P1:=ParentChart.Canvas.Calculate3DPosition(tmpBottom,EndZ);
          P2:=ParentChart.Canvas.Calculate3DPosition(TeePoint(BarBounds.Left,tmpT),EndZ);

          if TeeCull(P0,P1,P2) then
             ParentChart.Canvas.ZLine3D(
               {$IFDEF FMX}Round{$ENDIF}(BarBounds.Left),TickPos,StartZ,EndZ)
        end
        else
           ParentChart.Canvas.ZLine3D(
             {$IFDEF FMX}Round{$ENDIF}(BarBounds.Right),TickPos,StartZ,EndZ);
      end;
  end;
end;

Function TBarSeries.CalcBarBounds(ValueIndex:Integer):TRect;

  function PenWidth:Integer;
  begin
    if Pen.Visible then result:=Max(1,{$IFDEF FMX}Round{$ENDIF}(Pen.Width))
                   else result:=0;
  end;

begin
  if Assigned(FOnCalcBounds) then
     FOnCalcBounds(Self,ValueIndex,result)
  else
  with result do
  Begin
    Left:=CalcXPos(ValueIndex);

    if (BarWidthPercent=100) and (FCustomBarSize=0) and
       (FMultiBar<>mbSelfStack) and (FMultiBar<>mbSide) then
    begin
      if GetHorizAxis.Inverted then
         if ValueIndex>0 then
            Right:=CalcXPos(Pred(ValueIndex))
         else
            Right:=Left+IBarSize+PenWidth
      else
         if ValueIndex<Count-1 then
            Right:=CalcXPos(Succ(ValueIndex))+1-(PenWidth div 2)
         else
            Right:=Left+IBarSize+1-(PenWidth div 2);
    end
    else
       Right:=Left+IBarSize+1;

    Top   :=CalcYPos(ValueIndex);
    Bottom:=GetOriginPos(ValueIndex);

    if not BarPen.Visible then
    begin
      if Bottom>Top then
         Bottom:=Bottom+1
      else
         Top:=Top+1;

      Right:=Right+1;
    end;
  end;
end;

Function TBarSeries.CalcYPos(ValueIndex:Integer):Integer;
var tmp      : Double;
    tmpValue : Double;
Begin
  Case FMultiBar of
    mbNone,mbSide,mbSideAll: result:=inherited CalcYPos(ValueIndex)
  else
  begin
    tmpValue:=YValues.Value[ValueIndex]+PointOrigin(ValueIndex,False);
    if (FMultiBar=mbStacked) or (FMultiBar=mbSelfStack) then
       result:=CalcYPosValue(tmpValue)
    else
    begin
      tmp:=PointOrigin(ValueIndex,True);
      if tmp<>0 then result:=CalcYPosValue(tmpValue*100.0/tmp)
                else result:=0;
    end;
  end;
  end;
End;

Function TBarSeries.GetOriginPos(ValueIndex:Integer):Integer;
Begin
  result:=InternalGetOriginPos(ValueIndex,GetVertAxis.IEndPos);
end;

Function TBarSeries.InternalClicked(ValueIndex:Integer; P:TPoint):Boolean;
var tmpX : Integer;

  Function InTriangle(Y1,Y2:Integer):Boolean;
  var TriP : TTrianglePoints;
  begin
    if ParentChart.View3D then
    begin
      with ParentChart.Canvas do
      begin
        TriP[0]:=Calculate3DPosition(tmpX,Y1,StartZ);
        TriP[1]:=Calculate3DPosition(tmpX+(IBarSize div 2),Y2,MiddleZ);
        TriP[2]:=Calculate3DPosition(tmpX+IBarSize,Y1,StartZ);
      end;

      result:=PointInPolygon(P,TriP);
    end
    else
      result:=PointInTriangle(P,tmpX,tmpX+IBarSize,Y1,Y2);
  end;

  Function InArrow(Y1,Y2:Integer):Boolean;
  var P1,P2: TPoint;
      tmpP : TPointArray;
      tmpMidX : Integer;
  begin
    tmpMidX:=tmpX+(IBarSize div 2);

    with ParentChart.Canvas do
    if ParentChart.View3D then
    begin
      P1:=Calculate3DPosition(tmpMidX,Y1,MiddleZ);
      P2:=Calculate3DPosition(tmpMidX,Y2,MiddleZ);
      tmpP:=GetArrowPoints(50,P1,P2,IBarSize,IBarSize div 2,MiddleZ);
    end
    else
      tmpP:=GetArrowPoints(50,TeePoint(tmpMidX,Y1),TeePoint(tmpMidX,Y2),IBarSize,IBarSize div 2);

    result:=PointInPolygon(P,tmpP);
  end;

var tmpY : Integer;
    endY : Integer;

  Function OtherClicked:Boolean;
  begin
    if BarStyle=bsEllipse then
       result:=PointInEllipse(P,tmpX,tmpY,tmpX+IBarSize,endY)
    else
       result:=(P.Y>=tmpY) and (P.Y<=endY);
  end;

begin
  result:=False;
  tmpX:=CalcXPos(ValueIndex);

  if (not ParentChart.View3D) and
     ((P.X<tmpX) or (P.X>(tmpX+IBarSize))) then exit;

  tmpY:=CalcYPos(ValueIndex);
  endY:=GetOriginPos(ValueIndex);
  if endY<tmpY then SwapInteger(tmpY,endY);

  Case BarStyle of
   bsInvPyramid,
      bsInvCone : result:=InTriangle(tmpY,endY);
      bsPyramid,
      bsCone    : result:=InTriangle(endY,tmpY);
      bsArrow   : result:=InArrow(endY,tmpY);
      bsInvArrow: result:=InArrow(tmpY,endY);
      bsDiamond : result:=PointInDiamond(P,tmpX,tmpY,tmpX+IBarSize,endY);
   else
     if ParentChart.View3D then
        result:=PointInCube(P,TeeRect(tmpX,tmpY,tmpX+IBarSize,endY),StartZ,EndZ)
     (*
     begin
       tmpPX:={$IFDEF FMX}Round{$ENDIF}(P.X);
       tmpPY:={$IFDEF FMX}Round{$ENDIF}(P.Y);

       ParentChart.Canvas.Calculate2DPosition(tmpPX, tmpPY, StartZ);
       P.Y:=tmpPY;

       if ((tmpPX>=tmpX) and (tmpPX<=(tmpX+IBarSize))) then
          result:=OtherClicked;
     end
     *)
     else
       result:=OtherClicked;
   end;
end;

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

  SetHorizontal;
  XValues.Order:=loNone;
  YValues.Order:=loAscending;
  Gradient.Direction:=gdLeftRight;
  MandatoryValueList.Name:=TeeMsg_ValuesBar;
end;

Procedure THorizBarSeries.CalcHorizMargins(var LeftMargin,RightMargin:Integer);
var tmp : Integer;
begin
  inherited;

  if FMarksOnBar then
     tmp:=0
  else
     tmp:=CalcMarkLength(TeeAllValues);

  if Pen.Visible then
     tmp:=Max(tmp,2+Round(Pen.Width*0.5));

  if tmp>0 then
     Inc(tmp);

  if (FUseOrigin and (inherited MinXValue<FOrigin)) then
     Inc(LeftMargin,tmp);

  if (not FUseOrigin) or (inherited MaxXValue>FOrigin) then
     if GetHorizAxis.Inverted then
        Inc(LeftMargin,tmp)
     else
        Inc(RightMargin,tmp);
end;

Procedure THorizBarSeries.CalcVerticalMargins(var TopMargin,BottomMargin:Integer);
begin
  inherited;
  InternalApplyBarMargin(TopMargin,BottomMargin);
end;

procedure THorizBarSeries.CalcSelectionPos(ValueIndex:Integer; out X,Y:Integer);
begin
  inherited;
  Inc(Y,(IBarSize shr 1));
end;

Function THorizBarSeries.CalcXPos(ValueIndex:Integer):Integer;
var tmp      : Double;
    tmpValue : Double;
Begin
  if (FMultiBar=mbNone) or (FMultiBar=mbSide) or (FMultiBar=mbSideAll) then
     result:=inherited CalcXPos(ValueIndex)
  else
  begin
    tmpValue:=XValues.Value[ValueIndex]+PointOrigin(ValueIndex,False);
    if (FMultiBar=mbStacked) or (FMultiBar=mbSelfStack) then
       result:=CalcXPosValue(tmpValue)
    else
    begin
      tmp:=PointOrigin(ValueIndex,True);
      if tmp<>0 then result:=CalcXPosValue(tmpValue*100.0/tmp)
                else result:=0;
    end;
  end;
End;

Function THorizBarSeries.GetOriginPos(ValueIndex:Integer):Integer;
Begin
  result:=InternalGetOriginPos(ValueIndex,GetHorizAxis.IStartPos);
end;

{ The vertical Bar position is the "real" Y pos +
  the Barwidth by our BarSeries order }
Function THorizBarSeries.CalcYPos(ValueIndex:Integer):Integer;
Begin
  if FMultiBar=mbSideAll then
     result:=GetVertAxis.CalcYPosValue(IPreviousCount+ValueIndex)-(IBarSize shr 1)
  else
  if FMultiBar=mbSelfStack then
     result:=(inherited CalcYPosValue(MinYValue))-(IBarSize shr 1)
  else
  begin
    result:=inherited CalcYPos(ValueIndex);

    if FMultiBar<>mbNone then
       result:=result+Round(IBarSize*( ((INumBars*0.5)-(1+INumBars-IOrderPos)) ))
    else
       result:=result-(IBarSize shr 1);
  end;

  result:=ApplyBarOffset(result);
end;

Function THorizBarSeries.InternalClicked(ValueIndex:Integer; P:TPoint):Boolean;
var tmpY : Integer;

  Function InTriangle(X1,X2:Integer):Boolean;
  var TriP : TTrianglePoints;
  begin
    if ParentChart.View3D then
    begin
      with ParentChart.Canvas do
      begin
        TriP[0]:=Calculate3DPosition(X1,tmpY,StartZ);
        TriP[1]:=Calculate3DPosition(X2,tmpY+(IBarSize div 2),MiddleZ);
        TriP[2]:=Calculate3DPosition(X1,tmpY+IBarSize,StartZ);
      end;
      result:=PointInPolygon(P,TriP);
    end
    else result:=PointInHorizTriangle(P,tmpY,tmpY+IBarSize,X1,X2);
  end;

  Function InArrow(X1,X2:Integer):Boolean;
  var P1,P2: TPoint;
      tmpP : TPointArray;
  begin
    with ParentChart.Canvas do
    if ParentChart.View3D then
    begin
      P1:=Calculate3DPosition(X1,tmpY+(IBarSize div 2),MiddleZ);
      P2:=Calculate3DPosition(X2,tmpY+(IBarSize div 2),MiddleZ);
      tmpP:=GetArrowPoints(50,P1,P2,IBarSize,IBarSize div 2,MiddleZ);
    end
    else
    begin
      P1:=TeePoint(X1,tmpY+(IBarSize div 2));
      P2:=TeePoint(X2,tmpY+(IBarSize div 2));
      tmpP:=GetArrowPoints(50,P1,P2,IBarSize,IBarSize div 2);
    end;
    result:=PointInPolygon(P,tmpP);
  end;

var tmpX : Integer;
    endX : Integer;

  Function OtherClicked:Boolean;
  begin
    if BarStyle=bsEllipse then
       result:=PointInEllipse(P,tmpX,tmpY,endX,tmpY+IBarSize)
    else
       result:=(P.X>=tmpX) and (P.X<=endX);
  end;

begin
  result:=False;

  tmpY:=CalcYPos(ValueIndex);

  if (not ParentChart.View3D) and
     ((P.Y<tmpY) or (P.Y>(tmpY+IBarSize))) then exit;

  tmpX:=CalcXPos(ValueIndex);
  endX:=GetOriginPos(ValueIndex);
  if endX<tmpX then SwapInteger(tmpX,endX);

  Case FBarStyle of
   bsInvPyramid,
      bsInvCone : result:=InTriangle(endX,tmpX);
      bsPyramid,
      bsCone    : result:=InTriangle(tmpX,endX);
      bsArrow   : result:=InArrow(tmpX,endX);
      bsInvArrow: result:=InArrow(endX,tmpX);
      bsDiamond : result:=PointInDiamond(P,tmpX,tmpY,endX,tmpY+IBarSize);
   else
     if ParentChart.View3D then
        result:=PointInCube(P,TeeRect(tmpX,tmpY,endX,tmpY+IBarSize),StartZ,EndZ)
     (*
     begin
       tmpPX:={$IFDEF FMX}Round{$ENDIF}(P.X);
       tmpPY:={$IFDEF FMX}Round{$ENDIF}(P.Y);

       ParentChart.Canvas.Calculate2DPosition(tmpPX, tmpPY, StartZ);
       P.X:=tmpPX;

       if ((tmpPY>=tmpY) and (tmpPY<=(tmpY+IBarSize))) then
          result:=OtherClicked;
     end
     *)
     else
       result:=OtherClicked;
  end;
end;

function THorizBarSeries.InvertedLegend:Boolean;
begin
  result:=not GetVertAxis.Inverted;
end;

Function THorizBarSeries.MaxXValue:TChartValue;
Begin
  result:=MaxMandatoryValue(inherited MaxXValue);
end;

Function THorizBarSeries.MinXValue:TChartValue;
Begin
  result:=MinMandatoryValue(inherited MinXValue);
end;

Function THorizBarSeries.MaxYValue:TChartValue;
Begin
  case FMultiBar of
    mbSideAll  : result:=IPreviousCount+Count-1;
    mbSelfStack: result:=MinYValue;
  else result:=inherited MaxYValue;
  end;
end;

Function THorizBarSeries.InternalCalcMarkLength(ValueIndex:Integer):Integer;
Begin
  if ValueIndex=TeeAllValues then result:=MaxMarkWidth
                             else result:=TSeriesMarksAccess(Marks).TextWidth(ValueIndex);
end;

Procedure THorizBarSeries.DrawMark( ValueIndex:Integer; Const St:String;
                                    APosition:TSeriesMarkPosition);
var DifH : Integer;
    DifW : Integer;
    tmp  : Boolean;
    tmpBounds: TRect;
    tmpRotateSize : Integer;
    tmpMargin : TCoordinate;
    tmpAngle : Integer;
Begin
  With APosition do
  begin
    DifH:=IBarSize div 2;
    DifW:=Marks.Callout.Length+Marks.Callout.Distance;

    if not MarksOnBar then
    begin
      tmp:=ArrowFrom.X<GetOriginPos(ValueIndex);
      if tmp then DifW:=-DifW-Width;

      LeftTop.X:=LeftTop.X+DifW+(Width {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});
      LeftTop.Y:=LeftTop.Y+DifH+(Height {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});

      ArrowTo.X:=ArrowTo.X+DifW;
      ArrowTo.Y:=ArrowTo.Y+DifH;
      ArrowFrom.Y:=ArrowFrom.Y+DifH;

      if tmp then
         ArrowFrom.X:=ArrowFrom.X-Marks.Callout.Distance
      else
         ArrowFrom.X:=ArrowFrom.X+Marks.Callout.Distance;
    end
    else
    begin
      TSeriesMarksAccess(Marks).Arrow.Visible:=False;
      LeftTop.Y:=LeftTop.Y+DifH + (Height {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});
      tmpBounds:=CalcBarBounds(ValueIndex);
      tmpAngle:=TSeriesMarksAccess(Marks).Angle;

      if MarksLocation=mlCenter then
      begin
        LeftTop.X := ((tmpBounds.Right+tmpBounds.Left) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}) -
                     APosition.Width {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
      end
      else
      begin
        if Marks.Transparent then
           Marks.Margins.Calculate(APosition.Width,APosition.Height,{$IFDEF FMX}Round{$ENDIF}(4*Marks.Font.Size));

        if (tmpAngle=0) or (tmpAngle=360) then
           tmpMargin:=Marks.Margins.Size.Left
        else
        if tmpAngle=180 then
           tmpMargin:=Marks.Margins.Size.Right
        else
           tmpMargin:=0;

        tmpRotateSize:={$IFDEF FMX}Round{$ENDIF}(tmpMargin);

        if MarksLocation=mlStart then
           if GetHorizAxis.Inverted then
              LeftTop.X:=tmpBounds.Left - tmpRotateSize - APosition.Width
           else
              LeftTop.X:=tmpBounds.Left + tmpRotateSize
        else
        // mlEnd
           if GetHorizAxis.Inverted then
              LeftTop.X:=tmpBounds.Right + tmpRotateSize
           else
              LeftTop.X:=tmpBounds.Right - tmpRotateSize - APosition.Width;
      end;
    end;
  end;

  inherited;
end;

Procedure THorizBarSeries.DrawBar(BarIndex,StartPos,EndPos:Integer);
var tmpMidY : Integer;
    tmp     : TBarStyle;
    tmpSize : Integer;
    OldSlant,
    OldEllipse,
    Old     : Integer;
    tmpSwap : Boolean;

    {$IFDEF FMX}
    BoundsInt : TRectInteger;
    {$ENDIF}
Begin
  if (not Assigned(ParentChart)) or (not Assigned(ParentChart.Canvas.Brush)) then
     Exit;

  tmp:=GetBarStyle(BarIndex);

  if tmp=bsRectGradient then
     Gradient.Visible:=True;

  if Gradient.Visible then
     tmpSwap:=CalcBarGradient(BarIndex)
  else
     tmpSwap:=False;

  SetPenBrushBar(NormalBarColor,BarIndex);

  if Pen.Visible then
  begin
    tmpSize:={$IFDEF FMX}Round(Pen.Width*0.5){$ELSE}(Pen.Width div 2){$ENDIF};

    if tmpSize>0 then
       InflateRect(FBarBounds,-tmpSize,-tmpSize);
  end;

  {$IFDEF FMX}
  BoundsInt.Left:=Round(BarBounds.Left);
  BoundsInt.Top:=Round(BarBounds.Top);
  BoundsInt.Right:=Round(BarBounds.Right);
  BoundsInt.Bottom:=Round(BarBounds.Bottom);
  {$ENDIF}

  with ParentChart,Canvas,{$IFDEF FMX}BoundsInt{$ELSE}BarBounds{$ENDIF} do
  begin
    tmpMidY:=(Top+Bottom) div 2;

    if View3D then
    begin
      Old:=TeeNumCylinderSides;
      OldEllipse:=NumCirclePoints;
      TeeNumCylinderSides:=Max(2,Self.Sides);

      Case tmp of
        bsRectangle,
        bsRectGradient,
        bsBevel      : Cube(StartPos,EndPos,Top,Bottom,StartZ,EndZ,FDark3D);
     bsRoundRectangle: Cube(StartPos,EndPos,Top,Bottom,StartZ,EndZ,FDark3D,RoundSize);
        bsPyramid    : Pyramid(False,StartPos,Top,EndPos,Bottom,StartZ,EndZ,FDark3D);
        bsInvPyramid : Pyramid(False,EndPos,Top,StartPos,Bottom,StartZ,EndZ,FDark3D);
        bsCylinder   : Cylinder(False,Left,Top,Right,Bottom,StartZ,EndZ,FDark3D);
        bsEllipse    : EllipseWithZ(Left,Top,Right,Bottom,MiddleZ);
        bsArrow      : Arrow(True, TeePoint(StartPos,tmpMidY),
                                   TeePoint(EndPos,tmpMidY),
                                   Bottom-Top,
                                   (Bottom-Top) div 2,StartZ,EndZ,50);
        bsInvArrow   : Arrow(True, TeePoint(EndPos,tmpMidY),
                                   TeePoint(StartPos,tmpMidY),
                                   Bottom-Top,
                                   (Bottom-Top) div 2,StartZ,EndZ,50);
        bsCone       : Cone(False,StartPos,Top,EndPos,Bottom,StartZ,EndZ,FDark3D,ConePercent);
        bsInvCone    : Cone(False,EndPos,Top,StartPos,Bottom,StartZ,EndZ,FDark3D,ConePercent);
        bsSlantCube  :
        begin
           OldSlant:=TeeNumCylinderSides;
           TeeNumCylinderSides:=4;
           Cylinder(False,Left,Top,Right,Bottom,StartZ,EndZ,FDark3D);
           TeeNumCylinderSides:=OldSlant;
        end;
        bsDiamond    : Diamond(StartPos,Top,EndPos,Bottom,StartZ,EndZ);
      end;

      TeeNumCylinderSides:=Old;
      NumCirclePoints:=OldEllipse;
    end
    else
      Case tmp of
        bsRectangle,
        bsRectGradient,
        bsBevel,
        bsCylinder   : BarRectangle(NormalBarColor,BarBounds);
        bsRoundRectangle : BarRectangle(NormalBarColor,BarBounds,True);
        bsPyramid,
        bsCone       : Polygon([ TeePoint(StartPos,Top),
                     TeePoint(EndPos,tmpMidY),
                     TeePoint(StartPos,Bottom) ]);
        bsInvPyramid,
        bsInvCone    : Polygon([ TeePoint(EndPos,Top),
                     TeePoint(StartPos,tmpMidY),
                     TeePoint(EndPos,Bottom) ]);
        bsEllipse    : Ellipse(BarBounds);
        bsArrow      : Arrow(True, TeePoint(StartPos,tmpMidY),
                     TeePoint(EndPos,tmpMidY),
                     Bottom-Top,
                     (Bottom-Top) div 2,MiddleZ);
        bsInvArrow      : Arrow(True, TeePoint(EndPos,tmpMidY),
                     TeePoint(StartPos,tmpMidY),
                     Bottom-Top,
                     (Bottom-Top) div 2,MiddleZ);
        bsSlantCube  :
        begin
          BarRectangle(NormalBarColor,BarBounds);
          DoHorizLine(Left,Right,tmpMidY);
        end;
        bsDiamond    : Diamond(StartPos,Top,EndPos,Bottom,StartZ,EndZ);
      end;

    if tmpSwap then
       SwapGradientColors;

    if tmp=bsBevel then
       DrawBevel;
  end;

  DrawTickLines(StartPos,EndPos,tmp);
end;

Function THorizBarSeries.CalcBarBounds(ValueIndex:Integer):TRect;

  function PenWidth:Integer;
  begin
    if Pen.Visible then result:={$IFDEF FMX}Round{$ENDIF}(Pen.Width)
                   else result:=0;
  end;

begin
  if Assigned(FOnCalcBounds) then
     FOnCalcBounds(Self,ValueIndex,result)
  else
  with result do
  Begin
    Top:=CalcYPos(ValueIndex);

    if (BarWidthPercent=100) and (FCustomBarSize=0) and
        (FMultiBar<>mbSelfStack) and (FMultiBar<>mbSide) then
    begin
      if GetVertAxis.Inverted then
         if ValueIndex<Count-1 then
            Bottom:=CalcYPos(Succ(ValueIndex))
         else
            Bottom:=Top+IBarSize-PenWidth
      else
         if ValueIndex>0 then
            Bottom:=CalcYPos(Pred(ValueIndex))
         else
            Bottom:=Top+IBarSize-PenWidth
    end
    else
       Bottom:=Top+IBarSize-1;

    Right:=CalcXPos(ValueIndex);
    Left:=GetOriginPos(ValueIndex);

    if not BarPen.Visible then
    begin
      if Right>Left then
         Right:=Right+1
      else
      begin
        Left:=Left+1;
        Right:=Right-1;
      end;

      Bottom:=Bottom+1;
    end;
  end;
end;

function THorizBarSeries.MinYValue: TChartValue;
begin
  if MultiBar=mbSelfStack then
    if SelfStack.UsePosition then
       result:=SelfStack.Position
    else
       result:=SeriesIndex
  else
     result:=inherited MinYValue;
end;

function THorizBarSeries.DrawSeriesAscending:Boolean;
begin
  result:=inherited DrawSeriesAscending;

  if (FMultiBar=mbSideAll) or (FMultiBar=mbSelfStack) then
  with ParentChart.View3DOptions do
       if ((Rotation>0) and (Rotation<180)) then
          result:=not result;
end;

Function THorizBarSeries.DrawSeriesForward(ValueIndex:Integer):Boolean;
begin
  Case FMultiBar of
    mbNone    : result:=inherited DrawSeriesForward(ValueIndex);
    mbSide,
    mbSideAll : result:=False;
    mbSelfStack:
               begin
                 result:=MandatoryValueList.Value[ValueIndex]>=0;
                 if GetHorizAxis.Inverted then result:=not result;

                 // Fix for 3D rotation <180
                 if not inherited DrawSeriesForward(ValueIndex) then
                    result:=not result;
               end;
  else
  begin
    // mbStacked, mbStacked100
    result:=not GetHorizAxis.Inverted;

    // Fix for 3D rotation <180
    if not inherited DrawSeriesForward(ValueIndex) then
       result:=not result;
  end
  end;
end;

function THorizBarSeries.DrawValuesForward:Boolean;
begin
  result:=inherited DrawValuesForward;

  if FMultiBar=mbSelfStack then
  with ParentChart.View3DOptions do
       if ((Rotation>0) and (Rotation<180)) then
          result:=not result;
end;

procedure THorizBarSeries.DrawTickLine(TickPos: Integer; AStyle:TBarStyle);
var tmpL,
    tmpR,
    tmp : TCoordinate;
    P0,
    P1,
    P2 : TPoint;
    tmpZ : Integer;
begin
  case AStyle of
    bsRectangle,
    bsRectGradient,
    bsBevel,
    bsRoundRectangle:
    begin
      if ParentChart.View3D and ParentChart.View3DOptions.BackFaced then
         tmpZ:=EndZ
      else
         tmpZ:=StartZ;

      ParentChart.Canvas.VertLine3D(TickPos,
         {$IFDEF FMX}Round{$ENDIF}(BarBounds.Top),
         {$IFDEF FMX}Round{$ENDIF}(BarBounds.Bottom),
         tmpZ);
    end;

    bsArrow:
    begin
      tmp:={$IFDEF FMX}Round{$ENDIF}(BarBounds.Bottom-BarBounds.Top) div 4;
      ParentChart.Canvas.VertLine3D(TickPos,
         {$IFDEF FMX}Round{$ENDIF}(BarBounds.Top)+tmp,
         {$IFDEF FMX}Round{$ENDIF}(BarBounds.Bottom)-tmp,
         MiddleZ);
    end;

    bsCylinder:
      if (not ParentChart.View3D) or ParentChart.View3DOptions.Orthogonal then
         DrawCylinderTicks(TickPos);
  end;

  if ParentChart.View3D then
  case AStyle of
    bsRectangle,
    bsRectGradient,
    bsBevel,
    bsRoundRectangle:
      begin
        tmpR:=BarBounds.Right;
        tmpL:=BarBounds.Left;

        if tmpL>tmpR then
           {$IFDEF FMX}
           SwapSingle(tmpL,tmpR);
           {$ELSE}
           SwapInteger(tmpL,tmpR);
           {$ENDIF}

        P0:=ParentChart.Canvas.Calculate3DPosition(TeePoint(tmpL,BarBounds.Top),StartZ);
        P1:=ParentChart.Canvas.Calculate3DPosition(TeePoint(tmpL,BarBounds.Top),EndZ);
        P2:=ParentChart.Canvas.Calculate3DPosition(TeePoint(tmpR,BarBounds.Top),EndZ);

        if TeeCull(P0,P1,P2) then
        begin
          P0:=ParentChart.Canvas.Calculate3DPosition(TeePoint(tmpL,BarBounds.Bottom),StartZ);
          P1:=ParentChart.Canvas.Calculate3DPosition(TeePoint(tmpL,BarBounds.Bottom),EndZ);
          P2:=ParentChart.Canvas.Calculate3DPosition(TeePoint(tmpR,BarBounds.Bottom),EndZ);

          if TeeCull(P0,P1,P2) then
             ParentChart.Canvas.ZLine3D(TickPos,
                {$IFDEF FMX}Round{$ENDIF}(BarBounds.Top),StartZ,EndZ)
        end
        else
           ParentChart.Canvas.ZLine3D(TickPos, {$IFDEF FMX}Round{$ENDIF}(BarBounds.Top),
                                      StartZ,EndZ);
      end;

//    ParentChart.Canvas.ZLine3D(TickPos,BarBounds.Top,StartZ,EndZ);
  end;
end;

{ TCircledSeries }

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

  CalcVisiblePoints:=False; { always draw all points }
  XValues.Name:=TeeMsg_ValuesAngle;

  FCircleBrush:=TTeeBrush.Create(CanvasChanged);

  FEmboss:=TTeeEmboss.Create(CanvasChanged);
  TEmbossAccess(FEmboss).InitValues(FEmboss.Visible,Tee_CircledEmbossColor,3);

  FShadow:=TTeeShadow.Create(CanvasChanged);
  TShadowAccess(FShadow).InitValues(FShadow.Visible,Tee_CircledShadowColor,3);

  IAdjustCircleForMarks:=True;

  FCircled:=True;

  FUniqueCustomRadius:=True;
end;

Destructor TCircledSeries.Destroy;
begin
  FCircleBrush.Free;
  FEmboss.Free;
  FFrame.Free;
  FShadow.Free;
  SetParentProperties(True);
  FreeAndNil(IBack3D);

  inherited;
end;

procedure TCircledSeries.SetCircled(Value:Boolean);
var t : Integer;
Begin
  SetBooleanProperty(FCircled,Value);

  if Assigned(ParentChart) then
  with ParentChart do
  for t:=0 to SeriesCount-1 do
    if Self is Series[t].ClassType then
      with TCircledSeries(Series[t]) do
      begin
        FCircled:=Value;

        if not (csLoading in ComponentState) then
        begin
          FCustomXRadius:=0;
          FCustomYRadius:=0;
        end;
      end;
end;

{$IFDEF FMX}
function RGB(R,G,B:Byte):TColor;
begin
  result:=MakeColor(R,G,B);
end;
{$ENDIF}

function TCircledSeries.GetFrame:TFramedBorder;

  procedure InitGradient(const AGradient:TTeeGradient; const AStart,AMid,AEnd:TColor);
  begin
    AGradient.StartColor:=AStart;
    AGradient.MidColor:=AMid;
    AGradient.EndColor:=AEnd;
  end;

var Old : Boolean;
begin
  if not Assigned(FFrame) then
  begin
    FFrame:=TCircularFrame.Create;
    TCircularFrame(FFrame).SetDefaultCircled(True);

    FFrame.ParentChart:=ParentChart;

    if Assigned(ParentChart) then
    begin
      Old:=ParentChart.AutoRepaint;
      ParentChart.AutoRepaint:=False;
    end
    else
      Old:=False;

    FFrame.Width:=4;
    FFrame.Visible:=False;
    TCircularFrame(FFrame).DefaultVisible:=False;

    InitGradient(FFrame.OuterBrush.Gradient,
                   {$IFDEF FMX}TAlphaColors.LtGray{$ELSE}clLtGray{$ENDIF},
                   {$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF},
                   RGB(64,64,64));

    InitGradient(FFrame.MiddleBrush.Gradient,
                   {$IFDEF FMX}TAlphaColors.Gray{$ELSE}clGray{$ENDIF},
                   {$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF},
                   RGB(130,130,130));

    InitGradient(FFrame.InnerBrush.Gradient,
                   RGB(64,64,64),
                   {$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF},
                   {$IFDEF FMX}TAlphaColors.Gray{$ELSE}clGray{$ENDIF});

    if Assigned(ParentChart) then
       ParentChart.AutoRepaint:=Old;
  end;

  result:=FFrame;
end;

procedure TCircledSeries.SetFrame(const Value:TFramedBorder);
begin
  if Assigned(Value) then Frame.Assign(Value)
                     else FreeAndNil(FFrame);
end;

function TCircledSeries.HasFrame:Boolean;
begin
  result:=Assigned(FFrame);
end;

procedure TCircledSeries.DrawFrame;
var tmp : TCoordinate;
begin
  if Assigned(FFrame) and (not ParentChart.View3D) and FFrame.Visible then
  begin
    Shadow.Visible:=False;

    TCircularFrame(FFrame).Draw(FCircleRect);

    tmp:={$IFNDEF FMX}Round{$ENDIF}(FFrame.CalcWidth(FCircleRect));

    {$IFDEF TEENORECTOFFSET}
    FCircleRect.Left:=FCircleRect.Left+tmp;
    FCircleRect.Top:=FCircleRect.Top+tmp;
    FCircleRect.Right:=FCircleRect.Right-tmp;
    FCircleRect.Bottom:=FCircleRect.Bottom-tmp;
    {$ELSE}
    Offset(FCircleRect.TopLeft,tmp);
    Offset(FCircleRect.BottomRight,-tmp);
    {$ENDIF}

    SetCircleRect(FCircleRect);
  end;
end;

Procedure TCircledSeries.SetRotationAngle(const Value:Integer);
Begin
  SetIntegerProperty(FRotationAngle,Value mod 360);
  IRotDegree:=FRotationAngle*TeePiStep;
end;

procedure TCircledSeries.SetOtherCustomRadius(IsXRadius:Boolean; Value:Integer);
var t : Integer;
Begin
  if Assigned(ParentChart) then
  with ParentChart do
  for t:=0 to SeriesCount-1 do
    if Self is Series[t].ClassType then
      With TCircledSeries(Series[t]) do
       if IsXRadius then FCustomXRadius:=Value
                    else FCustomYRadius:=Value;
end;

procedure TCircledSeries.SetUniqueCustomRadius(Value:Boolean);
var t : Integer;
Begin
  SetBooleanProperty(FUniqueCustomRadius,Value);
  if Assigned(ParentChart) then
  with ParentChart do
  for t:=0 to SeriesCount-1 do
    if Self is Series[t].ClassType then
      TCircledSeries(Series[t]).FUniqueCustomRadius:=Value;
end;

Function TCircledSeries.UseAxis:Boolean;
begin
  result:=False;
end;

procedure TCircledSeries.SetCustomXRadius(Value:Integer);
Begin
  SetIntegerProperty(FCustomXRadius,Value);

  if FUniqueCustomRadius then
     SetOtherCustomRadius(True,Value);
End;

procedure TCircledSeries.SetCustomYRadius(Value:Integer);
Begin
  SetIntegerProperty(FCustomYRadius,Value);

  if FUniqueCustomRadius then
     SetOtherCustomRadius(False,Value);
End;

Procedure TCircledSeries.SetShadow(const Value:TTeeShadow);
begin
  FShadow.Assign(Value)
end;

Procedure TCircledSeries.SetEmboss(const Value:TTeeEmboss);
begin
  FEmboss.Assign(Value)
end;

Procedure TCircledSeries.SetParentProperties(EnableParentProps:Boolean);
begin
  if Assigned(ParentChart) then
  With ParentChart do
  if (not (csDestroying in ComponentState)) and
     ((not Assigned(Canvas)) or (not Canvas.SupportsFullRotation)) and
      Assigned(View3DOptions) then
  begin
    if EnableParentProps and
      ((not Active) or (csDestroying in Self.ComponentState))
         then
           SetBackupProperties
    else
    if not Assigned(IBack3D) then
    begin
      IBack3D:=TView3DOptions.Create({$IFDEF TEEVCL}ParentChart{$ENDIF});
      IBack3D.Assign(View3DOptions);
      InitCustom3DOptions;
    end;
  end;
end;

Procedure TCircledSeries.InitCustom3DOptions;
begin
  // Nothing
end;

function TCircledSeries.IsPointInChartRect(ValueIndex: Integer): Boolean;
begin
  result:=True;
end;

function TCircledSeries.GetCircleGradient:TTeeGradient;
begin
  result:=FCircleBrush.Gradient;
end;

Procedure TCircledSeries.SetBackupProperties;
begin
  if Assigned(ParentChart) then
  begin
    ParentChart.View3DOptions.Assign(IBack3D);
    FreeAndNil(IBack3D);
  end;
end;

procedure TCircledSeries.SetCircleRect(const ARect:TRect);
begin
  FCircleRect:=ARect;
  AdjustCircleRect;
  CalcRadius;
end;

Procedure TCircledSeries.SetParentChart(Const Value:TCustomAxisPanel);
Begin
  if Value=nil then SetBackupProperties;

  if Value<>ParentChart then
  begin
    inherited;

    if Visible and Assigned(ParentChart) then
       SetParentProperties(False);
  end;
end;

Procedure TCircledSeries.Rotate(const Angle:Integer);
Begin
  RotationAngle:=(RotationAngle+Angle) mod 360;
End;

Function TCircledSeries.AssociatedToAxis(const Axis:TChartAxis):Boolean;
Begin
  result:=True;
end;

Procedure TCircledSeries.AdjustCircleRect;
Begin
  with FCircleRect do
  Begin
    if Odd({$IFDEF FMX}System.Round{$ENDIF}(Right-Left)) then Right:=Right-1;
    if Odd({$IFDEF FMX}System.Round{$ENDIF}(Bottom-Top)) then Bottom:=Bottom-1;

    if (Right-Left)<4 then Right:=Left+4;
    if (Bottom-Top)<4 then Bottom:=Top+4;
  end;

  RectSize(FCircleRect, FCircleWidth, FCircleHeight);
  RectCenter(FCircleRect,FCircleXCenter,FCircleYCenter);
End;

// If there's another Circled series in Chart,
// adjusts the circle rectangle using all series.
Procedure TCircledSeries.CheckOtherSeriesMarks;
var t : Integer;
    tmp : TChartSeries;
begin
  if Assigned(ParentChart) then
  with ParentChart do
  for t:=0 to SeriesCount-1 do
  begin
    tmp:=Series[t];

    if tmp.Active and SameClass(tmp) and tmp.Marks.Visible then
    begin
      if t>SeriesIndex then
         TSeriesAccess(tmp).DoBeforeDrawValues;

      Self.CircleRect:=TCircledSeries(tmp).CircleRect;
      //AdjustCircleRect;
      //CalcRadius;

      break;
    end;
  end;
end;

class Procedure TCircledSeries.AdjustScreenRatio(const ACanvas:TCanvas3D;
             AXRadius,AYRadius:Integer; var R:TRect; isPrint : Boolean);

    function AdjustRatio(Const ARatio:Double):Double;
    var tmpRatio : Double;
    begin
      tmpRatio:=ScreenRatio(ACanvas);
      if tmpRatio=0 then result:=ARatio
                    else result:=1.0*ARatio/tmpRatio;
    end;

var Ratio : Double;
    Dif   : TCoordinate;
    P     : TPoint;
begin
  if isPrint then
    Ratio:=1
  else
  Begin
    P:=TeeGetScreenSize;
    if P.Y=0 then Ratio:=1
             else Ratio:=AdjustRatio(P.X/P.Y);
  end;

  if Round(Ratio*AYRadius)<AXRadius then
  Begin
    dif:=(AXRadius-{$IFNDEF FMX}Round{$ENDIF}(Ratio*AYRadius));
    R.Left:=R.Left+Dif;
    R.Right:=R.Right-Dif;
  end
  else
  Begin
    dif:=(AYRadius-{$IFNDEF FMX}Round{$ENDIF}(1.0*AXRadius/Ratio));
    R.Top:=R.Top+Dif;
    R.Bottom:=R.Bottom-Dif;
  end;
end;

Procedure TCircledSeries.DoBeforeDrawValues;

  Procedure CalcCircledRatio;
  Begin
    CalcRadius;
    AdjustScreenRatio(ParentChart.Canvas,FXRadius,FYRadius,FCircleRect,False);
    AdjustCircleRect;
  end;

  Procedure AdjustCircleMarks;
  var tmpH,
      tmpW,
      tmpFrame : TCoordinate;
  begin
    tmpFrame:=Marks.Callout.Length;

    With Marks.Frame do
    if Visible then
       tmpFrame:=tmpFrame+2*Width;

    Marks.Text:=' ';
    TSeriesMarksAccess(Marks).CalcBounds(ParentChart);
    tmpH:=Marks.Height+tmpFrame;

    //TTeePanelAccess(ParentChart).TryAssignFont(Marks.Font);

    With ParentChart do
    Begin
      //tmpH:=Canvas.FontHeight+tmpFrame;
      FCircleRect.Top:=FCircleRect.Top+tmpH;
      FCircleRect.Bottom:=FCircleRect.Bottom-tmpH;

      tmpW:=MaxMarkWidth+Canvas.TextWidth(TeeCharForHeight)+tmpFrame;
      FCircleRect.Left:=FCircleRect.Left+tmpW;
      FCircleRect.Right:=FCircleRect.Right-tmpW;

      AdjustCircleRect;
    end;
  end;

begin
  inherited;

  FCircleRect:=ParentChart.ChartRect;

  AdjustCircleRect;

  if Marks.Visible and IAdjustCircleForMarks then
     AdjustCircleMarks
  else
     CheckOtherSeriesMarks;

  if FCircled then
     CalcCircledRatio;

  CalcRadius;
end;

Procedure TCircledSeries.CalcRadius;
Begin
  // Pending: Inflate 0.5 if CirclePen.Width>1

  if CustomXRadius<>0 then
  Begin
    FXRadius:=CustomXRadius;
    FCircleWidth:=2*FXRadius;
  end
  else FXRadius:=FCircleWidth shr 1;

  if CustomYRadius<>0 then
  begin
    FYRadius:=CustomYRadius;
    FCircleHeight:=2*FYRadius;
  end
  else FYRadius:=FCircleHeight shr 1;

  with FCircleRect do
  begin
    Left  :=FCircleXCenter-FXRadius;
    Right :=FCircleXCenter+FXRadius;
    Top   :=FCircleYCenter-FYRadius;
    Bottom:=FCircleYCenter+FYRadius;
  end;
end;

function TCircledSeries.GetCircleBackColor:TColor;
begin
  result:=FCircleBrush.Color;
end;

procedure TCircledSeries.SetCircleBackColor(Value:TColor);
begin
  FCircleBrush.Color:=Value;
end;

procedure TCircledSeries.SetCircleBrush(const Value:TTeeBrush);
begin
  FCircleBrush.Assign(Value);
end;

Procedure TCircledSeries.AngleToPos( Const Angle,AXRadius,AYRadius:Double;
                                     out X,Y:Integer);
var tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
Begin
  SinCos(IRotDegree+Angle,tmpSin,tmpCos);

  X:=FCircleXCenter+Round(AXRadius*tmpCos);
  Y:=FCircleYCenter-Round(AYRadius*tmpSin);
end;

Procedure TCircledSeries.AngleToPos( Const Angle,AXRadius,AYRadius:Double;
                                     out X,Y:Single);
var tmpSin : {$IFDEF D15}Single{$ELSE}Extended{$ENDIF};
    tmpCos : {$IFDEF D15}Single{$ELSE}Extended{$ENDIF};
Begin
  SinCos(IRotDegree+Angle,tmpSin,tmpCos);
  X:=FCircleXCenter+(AXRadius*tmpCos);
  Y:=FCircleYCenter-(AYRadius*tmpSin);
end;

type
  TPanelAccess=class(TCustomTeePanelExtended);
  
Function TCircledSeries.CalcCircleBackColor:TColor;
begin
  result:=CircleBackColor;

  if result=clTeeColor then
     if ParentChart.Printing then
        result:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF}
     else
        with (ParentChart as TCustomChart).BackWall do
             if not Transparent then
                result:=Color;

  if result=clTeeColor then
     result:=TPanelAccess(ParentChart).GetBackColor;
end;

Procedure TCircledSeries.PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                              var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
Begin
  BackColor:=CalcCircleBackColor;
end;

Function TCircledSeries.PointToAngle(x,y:Integer):Double;
var tmpX, tmpY : Double;
begin
  if (x-FCircleXCenter)=0 then
  begin
    if y>FCircleYCenter then result:=-0.5*Pi{1.5*pi}
                        else result:=0.5*Pi;
  end
  else
  if (FXRadius=0) or (FYRadius=0) then result:=0
  else
  begin
    tmpY:=((FCircleYCenter-y)/FYRadius);
    tmpX:=((x-FCircleXCenter)/FXRadius);

    result:=ArcTan2(tmpY, tmpX);
  end;

  if result<0 then
     result:=2.0*Pi+result;

  result:=result-IRotDegree;

  if result<0 then
     result:=2.0*Pi+result;
end;

Function TCircledSeries.PointToRadius(x,y: Integer): Double;
var dx,dy,tmp: double;
begin
  With GetVertAxis do
  begin
    tmp := Maximum - Minimum;

    if tmp = 0 then
       result:=0
    else
    begin
      dx := (x-FCircleXCenter)*tmp/FXRadius;
      dy := (y-FCircleYCenter)*tmp/FYRadius;
      Result := Sqrt(Sqr(dx)+Sqr(dy))+Minimum;
    end
  end;
end;

Procedure TCircledSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TCircledSeries then
  With TCircledSeries(Source) do
  begin
    Self.FCircled            :=FCircled;
    Self.RotationAngle       :=FRotationAngle;
    Self.FCustomXRadius      :=CustomXRadius;
    Self.FCustomYRadius      :=CustomYRadius;
    Self.CircleBrush         :=FCircleBrush;
    Self.Emboss              :=Emboss;
    Self.Frame               :=FFrame;
    Self.Shadow              :=Shadow;
    Self.FUniqueCustomRadius :=FUniqueCustomRadius;
  end;

  inherited;
end;

Procedure TCircledSeries.SetActive(Value:Boolean);
begin
  inherited;
  SetParentProperties(not Active);
end;

procedure TCircledSeries.SetCircleGradient(const Value: TTeeGradient);
begin
  Brush.Gradient.Assign(Value);
end;

procedure TCircledSeries.DrawCircleShadow;
var tmpR : TRect;
    tmpW : Integer;
    tmpH : Integer;
begin
  if Shadow.Visible or Emboss.Visible then
  if (not ParentChart.View3D) or ParentChart.View3DOptions.Orthogonal then
  begin
    tmpW:=CircleWidth div 2;
    tmpH:=CircleHeight div 2;

    tmpR.Left:=CircleXCenter-tmpW;
    tmpR.Top:=CircleYCenter-tmpH;
    tmpR.Right:=CircleXCenter+tmpW;
    tmpR.Bottom:=CircleYCenter+tmpH;

    if ParentChart.View3D then
       tmpR:=ParentChart.Canvas.CalcRect3D(tmpR,EndZ);

    if Shadow.Visible then
       Shadow.DrawEllipse(ParentChart.Canvas,tmpR);

    if Emboss.Visible then
       Emboss.DrawEllipse(ParentChart.Canvas,tmpR);
  end;
end;

{ TSliceValueList }
Function TSliceValueList.Get(Index:Integer):Integer;
begin
  {$IFDEF D18}
  if Index<Count then
     result:=Items[Index]
  else
     result:=0;
  {$ELSE}

  if (Index<Count) and Assigned(Items[Index]) then
     result:=Integer(Items[Index])
  else
     result:=0
  {$ENDIF}
end;

Procedure TSliceValueList.Put(Index,Value:Integer);
const Zero={$IFNDEF D18}Pointer{$ENDIF}(0);
begin
  while Index>=Count do
        Add(Zero);

  {$IFDEF D18}
  if Items[Index]<>Value then
  begin
    Items[Index]:=Value;
    OwnerSeries.Repaint;
  end;
  {$ELSE}

  if Get(Index)<>Value then
  begin
    Items[Index]:=Pointer(Value);
    OwnerSeries.Repaint;
  end;

  {$ENDIF}
end;

{$IFDEF D18}
procedure TSliceValueList.Assign(const Source:TSliceValueList);
var t: Integer;
begin
  Clear;
  for t:=0 to Source.Count-1 do Add(Source[t]);
end;
{$ENDIF}

{$IFNDEF D6}
procedure TSliceValueList.Assign(const Source:TList);
var t: Integer;
begin
  Clear;
  for t:=0 to Source.Count-1 do Add(Source[t]);
end;
{$ENDIF}

{ TPieOtherSlice }

Constructor TPieOtherSlice.Create(const AOwner:TChartSeries);
begin
  inherited Create;
  FOwner:=AOwner;
  FColor:=clTeeColor;
end;

Destructor TPieOtherSlice.Destroy;
begin
  FLegend.Free;
  inherited;
end;

Procedure TPieOtherSlice.Assign(Source:TPersistent);
begin
  if Source is TPieOtherSlice then
  With TPieOtherSlice(Source) do
  begin
    Self.FColor:=FColor;

    if Assigned(FLegend) then Self.Legend:=Legend
                         else FreeAndNil(Self.FLegend);

    Self.FStyle:=FStyle;
    Self.FText :=FText;
    Self.FValue:=FValue;
  end;
end;

Function TPieOtherSlice.IsTextStored:Boolean;
begin
  result:=FText<>'';
end;

procedure TPieOtherSlice.SetColor(Value:TColor);
begin
  TSeriesAccess(FOwner).SetColorProperty(FColor,Value);
end;

procedure TPieOtherSlice.SetStyle(Value:TPieOtherStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;
    FOwner.Repaint;
  end;
end;

procedure TPieOtherSlice.SetText(Const Value:String);
begin
  if FText<>Value then
  begin
    FText:=Value;
    FOwner.Repaint;
  end;
end;

procedure TPieOtherSlice.SetValue(Const Value:Double);
begin
  TSeriesAccess(FOwner).SetDoubleProperty(FValue,Value);
end;

function TPieOtherSlice.GetText: String;
begin
  if FText='' then result:=TeeMsg_PieOther
              else result:=FText;
end;

function TPieOtherSlice.GetLegend: TChartLegend;
begin
  if not Assigned(FLegend) then
  begin
    FLegend:=TChartLegend.Create(FOwner.ParentChart);
    FLegend.Hide;
    FLegend.Series:=FOwner;
  end;

  result:=FLegend;
end;

procedure TPieOtherSlice.SetLegend(const Value: TChartLegend);
begin
  if Assigned(Value) then
  begin
   Legend.Assign(Value);
   FLegend.Series:=FOwner;
  end
  else FreeAndNil(FLegend);
end;

{ TPieMarks }

procedure TPieMarks.SetEmptySlice(const Value:Boolean);
begin
  TSeriesAccess(IParent).SetBooleanProperty(FEmptySlice,Value);
end;

procedure TPieMarks.SetInsideSlice(const Value:Boolean);
begin
  TSeriesAccess(IParent).SetBooleanProperty(FInsideSlice,Value);
end;

procedure TPieMarks.SetLegSize(const Value:Integer);
begin
  TSeriesAccess(IParent).SetIntegerProperty(FLegSize,Value);
end;

procedure TPieMarks.SetRotated(const Value:Boolean);
begin
  TSeriesAccess(IParent).SetBooleanProperty(FRotated,Value);
end;

procedure TPieMarks.SetRotateStyle(const Value:TRotateStyle);
begin
  if FRotateStyle<>Value then
  begin
    FRotateStyle:=Value;
    TSeriesAccess(IParent).Repaint;
  end;
end;

procedure TPieMarks.SetVertCenter(const Value:Boolean);
begin
  TSeriesAccess(IParent).SetBooleanProperty(FVertCenter,Value);
end;

Procedure TPieMarks.Assign(Source:TPersistent);
begin
  if Source is TPieMarks then
  with TPieMarks(Source) do
  begin
    Self.FEmptySlice:=FEmptySlice;
    Self.FInsideSlice:=FInsideSlice;
    Self.FLegSize:=FLegSize;
    Self.FRotated:=FRotated;
    Self.FRotateStyle:=FRotateStyle;
    Self.FVertCenter:=FVertCenter;
  end
  else inherited;
end;

{ TFrameBrush }

function TFrameBrush.IsSizeStored:Boolean;
begin
  result:=FSize<>IDefault;
end;

procedure TFrameBrush.InitSize(const ASize:Single);
begin
  FSize:=ASize;
  IDefault:=ASize;
end;

procedure TFrameBrush.SetSize(const Value:Single);
begin
  if FSize<>Value then
  begin
    FSize:=Value;
    Changed;
  end;
end;

{ TFramedBorder }

Constructor TFramedBorder.Create;
begin
  inherited Create;

  FWidth:=10; // %

  DefaultVisible:=True;

  FEmboss:=TTeeEmboss.Create(CanvasChanged);
  FShadow:=TTeeShadow.Create(CanvasChanged);

  FInner:=TFrameBrush.Create(CanvasChanged);
  FInner.InitSize(70);

  FMiddle:=TFrameBrush.Create(CanvasChanged);
  FMiddle.InitSize(10);

  FOuter:=TFrameBrush.Create(CanvasChanged);
  FOuter.InitSize(20);

  GetPalette;
end;

Destructor TFramedBorder.Destroy;
begin
  FOuter.Free;
  FMiddle.Free;
  FInner.Free;
  FEmboss.Free;
  FShadow.Free;

  inherited;
end;

procedure TFramedBorder.Assign(Source: TPersistent);
begin
  if Source is TFramedBorder then
  with TFramedBorder(Source) do
  begin
    Self.FCircled:=FCircled;
    Self.InnerBrush:=FInner;
    Self.MiddleBrush:=FMiddle;
    Self.OuterBrush:=FOuter;
    Self.Emboss:=Emboss;
    Self.Shadow:=Shadow;
    Self.FWidth:=FWidth;
  end;

  inherited;
end;

procedure TFramedBorder.ReadParentSeries(Reader:TReader); // obsolete
begin
  Reader.ReadIdent;
end;

procedure TFramedBorder.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineProperty('ParentSeries',ReadParentSeries,nil,False);  // obsolete
end;

procedure TFramedBorder.GetPalette;
begin
  FInner.BackColor:={$IFDEF FMX}TAlphaColors.Red{$ELSE}clRed{$ENDIF};
  FInner.Gradient.EndColor:=FInner.BackColor;

  FMiddle.BackColor:={$IFDEF FMX}TAlphaColors.Yellow{$ELSE}clYellow{$ENDIF};
  FMiddle.Gradient.EndColor:=FMiddle.BackColor;

  FOuter.BackColor:={$IFDEF FMX}TAlphaColors.Green{$ELSE}clGreen{$ENDIF};
  FOuter.Gradient.EndColor:=FOuter.BackColor;
end;

function TFramedBorder.CalcWidth(const R:TRect):Single;
begin
  result:=Width*Math.Min(R.Right-R.Left, R.Bottom-R.Top)*0.01;
end;

procedure TFramedBorder.Draw(R: TRect);
var
  tmp : TCanvas3D;
  tmpWidth : Single;

  procedure DrawBrush(const ABrush:TChartBrush);
  begin
    tmp.AssignBrush(ABrush,ABrush.Color);

    if Circled then
       tmp.Ellipse(R)
    else
       tmp.Rectangle(R);
  end;

  procedure DrawBand(const ABand:TFrameBrush);
  var tmpSize : Integer;
  begin
    tmpSize:=-Round(tmpWidth*ABand.FSize*0.01);

    if tmpSize<>0 then
       {$IFDEF D16}System.Types.{$ENDIF}InflateRect(R, tmpSize, tmpSize);

    if ABand.Style<>bsClear then
       DrawBrush(ABand);
  end;

  procedure TryDrawShadow(const AShadow:TTeeShadow);
  begin
    if AShadow.ShouldDraw then
       if Circled then
          AShadow.DrawEllipse(tmp,R)
       else
          AShadow.Draw(tmp,R);
  end;

begin
  tmpWidth:=CalcWidth(R);

  tmp:=ParentChart.Canvas;

  TryDrawShadow(Shadow);
  TryDrawShadow(Emboss);

  tmp.Pen.Style:=psClear;

  DrawBand(FOuter);

  tmpWidth:=CalcWidth(R);

  DrawBand(FMiddle);
  DrawBand(FInner);
  DrawBrush(Brush);
end;

function TFramedBorder.IsCircledStored:Boolean;
begin
  result:=Circled<>DefaultCircled;
end;

function TFramedBorder.IsVisibleStored:Boolean;
begin
  result:=Visible<>DefaultVisible;
end;

procedure TFramedBorder.SetCircled(const Value: Boolean);
begin
  if FCircled<>Value then
  begin
    FCircled:=Value;
    Repaint;
  end;
end;

procedure TFramedBorder.SetInner(const Value: TFrameBrush);
begin
  FInner.Assign(Value);
end;

procedure TFramedBorder.SetMiddle(const Value: TFrameBrush);
begin
  FMiddle.Assign(Value);
end;

procedure TFramedBorder.SetOuter(const Value: TFrameBrush);
begin
  FOuter.Assign(Value);
end;

procedure TFramedBorder.SetShadow(const Value: TTeeShadow);
begin
  FShadow.Assign(Value);
end;

procedure TFramedBorder.SetEmboss(const Value: TTeeEmboss);
begin
  FEmboss.Assign(Value);
end;

procedure TFramedBorder.SetWidth(const Value: Integer);
begin
  if FWidth<>Value then
  begin
    FWidth:=Value;
    Repaint;
  end;
end;

procedure TFramedBorder.SetDefaultCircled(const Value: Boolean);
begin
  FCircled:=Value;
  DefaultCircled:=FCircled;
end;

{ TCircularFrame }

Constructor TCircularFrame.Create;

  procedure PrepareGradient(const AGradient:TCustomTeeGradient);
  begin
    with AGradient do
    begin
      Visible:=True;
      MidColor:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};
      StartColor:=EndColor;
    end;
  end;

begin
  inherited;

  INoBrush:=True;

  FrameElementWidths[0] := 25;
  FrameElementWidths[1] := 60;
  FrameElementWidths[2] := 15;

  PrepareGradient(InnerBrush.Gradient);
  PrepareGradient(OuterBrush.Gradient);
  PrepareGradient(MiddleBrush.Gradient);

  FTotalAngle:=360;

//  ExtendInner := True;
end;

procedure RationaliseValues(var x,y,z: Double);
var
  tmpTotal, percentCoef: Double;
begin
  tmpTotal := (x + y) + z;

  if (tmpTotal <> 100.0) then
  begin
    percentCoef := 100.0 / tmpTotal;
    x := x * percentCoef;
    y := y * percentCoef;
    z := z * percentCoef;
  end;
end;

procedure TCircularFrame.Assign(Source:TPersistent);
begin
  if Source is TCircularFrame then
  with TCircularFrame(Source) do
  begin
    Self.FRotationAngle:=FRotationAngle;
    Self.FTotalAngle:=FTotalAngle;
  end;

  inherited;
end;

procedure TCircularFrame.Draw(R: TRect);

  procedure DrawFullBrush(const ABrush:TFrameBrush);
  var tmp : TTeeBasePath;
      tmpP : {$IFDEF FMX}TPoint{$ELSE}TPointFloat{$ENDIF};
  begin
    if ABrush.Style <> bsClear then
    with ParentChart.Canvas do
    begin
      AssignBrush(ABrush);
      Pen.Style := psClear;

      if Circled then
      begin
        if TotalAngle=360 then
           Ellipse(R)
        else
        begin
          tmp:=CreatePath;
          tmp.ArcTo(R.Left,R.Top,R.Right-R.Left,R.Bottom-R.Top,135+RotationAngle,TotalAngle);

          if TotalAngle<180 then
          begin
            tmpP:=PointFromCircle(R,135+RotationAngle+TotalAngle,0,True);
            tmp.MoveTo(tmpP.X,tmpP.Y);
            tmp.LineTo((R.Left+R.Right) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF},
                       (R.Top+R.Bottom) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});
          end;

          tmp.Close;
          tmp.Draw(ParentChart.Canvas);
          tmp.Free;
        end;
      end
      else
         Rectangle(R);
    end;
  end;

var
  tmpWidth : Single;

  procedure Reduce(const Amount:Single);
  var tmp : Integer;
  begin
    tmp:= -Round(tmpWidth * (Amount * 0.01));
    {$IFDEF D16}System.Types.{$ENDIF}InflateRect(R, tmp, tmp);
  end;

begin
  tmpWidth := CalcWidth(R);

  RationaliseValues(FrameElementWidths[0], FrameElementWidths[1],
                    FrameElementWidths[2]);

  ParentChart.Canvas.Brush.Assign(OuterBrush);

  DrawFullBrush(OuterBrush);

  ParentChart.Canvas.Brush.Assign(MiddleBrush);

  Reduce(FrameElementWidths[2]);

  DrawFullBrush(MiddleBrush);

  ParentChart.Canvas.Brush.Assign(InnerBrush);

  Reduce(FrameElementWidths[1]);

  DrawFullBrush(InnerBrush);

  Reduce(FrameElementWidths[0]);
end;

procedure TCircularFrame.SetTotalAngle(const Value: Integer);
begin
  if FTotalAngle<>Value then
  begin
    FTotalAngle:=Value;
    Repaint;
  end;
end;

procedure TCircularFrame.SetRotationAngle(const Value: Integer);
begin
  if FRotationAngle<>Value then
  begin
    FRotationAngle:=Value;
    Repaint;
  end;
end;

{ TPieBevel }

Constructor TPieBevel.Create(const AOwner:TChartSeries);
begin
  inherited Create;
  ISeries:=AOwner;
  FBright:=32;
  FUseBorder:=True;
end;

procedure TPieBevel.Assign(Source:TPersistent);
begin
  if Source is TPieBevel then
  with TPieBevel(Source) do
  begin
    Self.FBright    :=FBright;
    Self.FPercent   :=FPercent;
    Self.FUseBorder :=FUseBorder;
  end
  else
    inherited;
end;

procedure TPieBevel.SetUseBorder(const Value:Boolean);
begin
  TSeriesAccess(ISeries).SetBooleanProperty(FUseBorder,Value);
end;

procedure TPieBevel.SetBright(const Value:Integer);
begin
  TSeriesAccess(ISeries).SetIntegerProperty(FBright,Value);
end;

procedure TPieBevel.SetPercent(const Value:Integer);
begin
  TSeriesAccess(ISeries).SetIntegerProperty(FPercent,Value);
end;

{ TPieSeries }

type
 TGradientAccess=class(TCustomTeeGradient);

Const
  TeePieBelongsToOther = -1;
  TeePieOtherFlag      = MaxLongint;

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

  FAngleSize:=360;

  FBevel:=TPieBevel.Create(Self);

  FExplodedSlice:=TSliceValueList.Create;
  FExplodedSlice.OwnerSeries:=Self;

  FSliceHeights:=TSliceValueList.Create;
  FSliceHeights.OwnerSeries:=Self;

  FOtherSlice:=TPieOtherSlice.Create(Self);

  XValues.Name:='';
  YValues.Name:=TeeMsg_ValuesPie;

  Marks.Visible:=True;
  TShapeAccess(Marks).DefaultVisible:=True;

  Marks.Callout.Length:=8;
  TCalloutAccess(Marks.Callout).DefaultLength:=8;

  FDark3D:=True;
  ColorEachPoint:=True;
  IUseSeriesColor:=False;
  IUseNotMandatory:=False;

  CircleGradient.StartColor:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};

  TGradientAccess(CircleGradient).InitDirection(gdRadial);

  FMultiPie:=mpAutomatic;

  FPieMarks:=TPieMarks.Create;
  FPieMarks.IParent:=Self;
  FEdgeStyle:=edNone;

  Marks.Tail.AutoAlign := True;
  Marks.Tail.CustomPoint := True;
  Marks.Tail.Margin := 2;

  FCustomBounds:=TeeZeroRect;
end;

Destructor TPieSeries.Destroy;
begin
  FBevel.Free;
  FPieMarks.Free;
  FAngles:=nil;
  FreeAndNil(FSliceHeights);
  FreeAndNil(FExplodedSlice);
  FreeAndNil(FOtherSlice);

  inherited;
end;

Function TPieSeries.CalcXPos(ValueIndex:Integer):Integer;
begin
  if XValues.Value[ValueIndex]=TeePieOtherFlag then  { do not try to calc }
     result:=0
  else
     result:=inherited CalcXPos(ValueIndex);
end;

Function TPieSeries.NumSampleValues:Integer;
Begin
  result:=8;
End;

Procedure TPieSeries.ClearLists;
begin
  inherited;
  if Assigned(FExplodedSlice) then FExplodedSlice.Clear;
  if Assigned(FSliceHeights) then FSliceHeights.Clear;
end;

Procedure TPieSeries.PreparePiePen(ValueIndex:Integer);
var tmp : TCanvas3D;
begin
  if not PrepareHoverPen(ValueIndex) then
  begin
    tmp:=ParentChart.Canvas;

    if FDarkPen>0 then
       tmp.AssignVisiblePenColor(PiePen,ApplyDark(ValueColor[ValueIndex],255-FDarkPen))
    else
       tmp.AssignVisiblePen(PiePen);

    if tmp.Pen.Style<>psSolid then
       tmp.BackMode:=cbmTransparent;
  end;
end;

// Returns number of visible Pie series
function TPieSeries.PieCount:Integer;
var t : Integer;
    tmp : TChartSeries;
begin
  result:=0;

  for t:=0 to ParentChart.SeriesCount-1 do
  begin
    tmp:=ParentChart[t];

    if tmp.Active and SameClass(tmp) then
       Inc(result);
  end;
end;

Procedure TPieSeries.InitCustom3DOptions;
begin
  inherited;

  With ParentChart.View3DOptions do
  begin
    Orthogonal:=False;
    Rotation:=360;
    Elevation:=315;
    Perspective:=0;
    Tilt:=0;
  end;
end;

function TPieSeries.MoreSameZOrder:Boolean;
begin
  result:=False;
end;

Function TPieSeries.SliceBrushStyle(ValueIndex:Integer):{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF};
begin
  {$IFNDEF FMX}
  if (UsePatterns or ParentChart.Monochrome) and (ValueIndex<>TeeAllValues) then
     result:=GetDefaultPattern(ValueIndex)
  else
  {$ENDIF}
     result:=bsSolid;
end;

Procedure TPieSeries.PrepareLegendCanvas( ValueIndex:Integer; var BackColor:TColor;
                                          var BrushStyle:{$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
Begin
  inherited;
  PreparePiePen(ValueIndex);
  BrushStyle:=SliceBrushStyle(ValueIndex);
end;

Procedure TPieSeries.GalleryChanged3D(Is3D:Boolean);
begin
  inherited;
  DisableRotation;
end;

Procedure TPieSeries.AddSampleValues(NumValues:Integer; Sequential:Boolean=False);
var PieSampleStr : Array[0..7] of String;
    t : Integer;
Begin
  PieSampleStr[0]:=TeeMsg_PieSample1;
  PieSampleStr[1]:=TeeMsg_PieSample2;
  PieSampleStr[2]:=TeeMsg_PieSample3;
  PieSampleStr[3]:=TeeMsg_PieSample4;
  PieSampleStr[4]:=TeeMsg_PieSample5;
  PieSampleStr[5]:=TeeMsg_PieSample6;
  PieSampleStr[6]:=TeeMsg_PieSample7;
  PieSampleStr[7]:=TeeMsg_PieSample8;

  for t:=0 to NumValues-1 do
      Add( 1+RandomValue(ChartSamplesMax), { <-- Value }
           PieSampleStr[t mod 8]);      { <-- Label }
end;

Function TPieSeries.CalcClickedPie(x,y:Integer; Exploded:Boolean=True):Integer;
var tmpOffX : Integer;
    tmpOffY : Integer;

  function CalcPoint(const Angle,XR,YR:Double):TPoint;
  var tmpX : Integer;
      tmpY : Integer;
  begin
    AngleToPos(Angle, XR, YR, tmpX, tmpY);
    result:=ParentChart.Canvas.Calculate3DPosition(tmpX+tmpOffX, tmpY-tmpOffY, EndZ);
  end;

const
   CircleSteps=32;

var tmpAngle,
    tmpStep  : Double;
    t        : Integer;
    tmp      : Integer;
    P        : TPoint;
    Poly     : Array of TPoint;
    tmpXRadius,
    tmpYRadius : Double;
Begin
  if Length(FAngles)>0 then
  begin
    P.X:=X;
    P.Y:=Y;

    tmpOffX:=0;
    tmpOffY:=0;

    if DonutPercent>0 then
    begin
      SetLength(Poly,2*CircleSteps);

      tmpXRadius:=DonutPercent*XRadius*0.01;
      tmpYRadius:=DonutPercent*YRadius*0.01;
    end
    else
    begin
      SetLength(Poly,CircleSteps+1);
      tmpXRadius:=XRadius;
      tmpYRadius:=YRadius;
    end;

    for result:=0 to Count-1 do
    With FAngles[result] do
    begin
      if Exploded then
         CalcExplodedOffset(result,tmpOffX,tmpOffY);

      tmpStep:=(EndAngle-StartAngle)/Pred(CircleSteps);

      tmpAngle:=StartAngle;

      if DonutPercent=0 then
      begin
        // Exploded Center
        Poly[0]:=ParentChart.Canvas.Calculate3DPosition(CircleXCenter+tmpOffX,
                                                  CircleYCenter-tmpOffY, EndZ);

        for t:=1 to CircleSteps do
        begin
          Poly[t]:=CalcPoint(tmpAngle,XRadius,YRadius);
          tmpAngle:=tmpAngle+tmpStep;
        end;

      end
      else
         for t:=1 to CircleSteps do
         begin
           Poly[t]:=CalcPoint(tmpAngle,XRadius,YRadius);

           if t=1 then tmp:=0
                  else tmp:=2*CircleSteps-t+1;

           Poly[tmp]:=CalcPoint(tmpAngle,tmpXRadius,tmpYRadius);
           tmpAngle:=tmpAngle+tmpStep;
         end;

      if PointInPolygon(P,Poly) then
         Exit;
    end;
  end;

  result:=TeeNoPointClicked;
end;

Function TPieSeries.Clicked(x,y:Integer):Integer;
begin
  result:=inherited Clicked(x,y);

  if result=TeeNoPointClicked then
     result:=CalcClickedPie(x,y);
end;

Function TPieSeries.CountLegendItems:Integer;
var t : Integer;
begin
  result:=0;

  for t:=0 to Count-1 do
      if BelongsToOtherSlice(t) then
         Inc(result);

  if (not Assigned(OtherSlice.FLegend)) or (ILegend<>OtherSlice.FLegend) then
     result:=Count-result;
end;

Function TPieSeries.CalcLegendIndex(LegendIndex:Integer):Integer;
begin
  result:=LegendToValueIndex(LegendIndex);
end;

Function TPieSeries.LegendToValueIndex(LegendIndex:Integer):Integer;
var Num : Integer;
    t   : Integer;
    tmp : Boolean;
    tmpIsOther : Boolean;
begin
  result:=LegendIndex;
  Num:=-1;

  tmpIsOther:=Assigned(OtherSlice.FLegend) and (ILegend=OtherSlice.FLegend);

  for t:=0 to Count-1 do
  begin
    tmp:=BelongsToOtherSlice(t);

    if (tmpIsOther and tmp) or ((not tmpIsOther) and (not tmp)) then
    begin
      Inc(Num);

      if Num=LegendIndex then
      begin
        result:=t;
        break;
      end;
    end;
  end;
end;

Function TPieSeries.BelongsToOtherSlice(ValueIndex:Integer):Boolean;
begin
  result:=NotMandatoryValueList.Value[ValueIndex]=TeePieBelongsToOther;
end;

Procedure TPieSeries.CalcAngles;
const PiRatio=Pi*2.0/360.0;
var tmpSumAbs : Double;
    AcumValue : Double;
    PiPortion : Double;
    t         : Integer;
    TotalAngle: Double;
Begin
  TotalAngle:=PiRatio*FAngleSize;
  AcumValue:=0;

  if (OtherSlice.Style=poNone) and (FirstValueIndex<>-1) then
  begin
    tmpSumAbs:=0;

    for t:=FirstValueIndex to LastValueIndex do
        tmpSumAbs:=tmpSumAbs+Abs(MandatoryValueList.Value[t]);
  end
  else
    tmpSumAbs:=MandatoryValueList.TotalAbs;

  if tmpSumAbs<>0 then PiPortion:=TotalAngle/tmpSumAbs
                  else PiPortion:=0;

  SetLength(FAngles,Count);

  for t:=FirstValueIndex to LastValueIndex do
  With FAngles[t] do
  Begin
    if t=FirstValueIndex then StartAngle:=0
                         else StartAngle:=FAngles[t-1].EndAngle;

    if tmpSumAbs<>0 then
    Begin
      if not BelongsToOtherSlice(t) then
         AcumValue:=AcumValue+Abs(MandatoryValueList.Value[t]);

      if AcumValue=tmpSumAbs then EndAngle:=TotalAngle
                             else EndAngle:=AcumValue*PiPortion;

      { prevent small pie sectors }
      if (EndAngle-StartAngle)>TotalAngle then
         EndAngle:=StartAngle+TotalAngle;
    end
    else EndAngle:=TotalAngle;

    MidAngle:=(StartAngle+EndAngle)*0.5;
  end;
end;

Procedure TPieSeries.CalcExplodedRadius(ValueIndex:Integer; out AXRadius,AYRadius:Integer);
var tmpExp : Double;
begin
  if FExplodedSlice[ValueIndex]=0 then
  begin
    AXRadius:=FXRadius;
    AYRadius:=FYRadius;
  end
  else
  begin
    tmpExp:=1.0+FExplodedSlice[ValueIndex]*0.01;
    AXRadius:=Round(FXRadius*tmpExp);
    AYRadius:=Round(FYRadius*tmpExp);
  end;
end;

Function TPieSeries.SliceEndZ(ValueIndex:Integer):Integer;
begin
  if FSliceHeights.Count>ValueIndex then
     result:=StartZ+Round((EndZ-StartZ)*FSliceHeights[ValueIndex]*0.01)
  else
     result:=EndZ;
end;

function TPieSeries.ChangeColor(const AColor:TColor; ABright:Integer):TColor;
begin
  if ABright=0 then
     result:=AColor
  else
  if ABright>0 then
     result:=ApplyBright(AColor,ABright)
  else
     result:=ApplyDark(AColor,-ABright);
end;

procedure TPieSeries.SetGradientColor(const AGradient:TTeeGradient; AColor:TColor);
begin
  AGradient.EndColor:=AColor;
  AGradient.StartColor:=ChangeColor(AColor,GradientBright);
end;

Procedure TPieSeries.DrawLegendShape(ValueIndex:Integer; Const Rect:TRect);
var tmpColor : TColor;
begin
  if CircleGradient.Visible then
  begin
    if ParentChart.Monochrome then tmpColor:={$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF}
                              else tmpColor:=ValueColor[ValueIndex];

    if tmpColor<>clNone then
    begin
      ParentChart.Canvas.Gradient.Assign(CircleGradient);
      ParentChart.Canvas.Gradient.Visible:=True;

      SetGradientColor(ParentChart.Canvas.Gradient,tmpColor);
    end;
  end;

  inherited;
end;

Function TPieSeries.GetTailEnd(const x,y,margin,zPos:Integer; const Align:TTailAlign):TPoint;
Begin
  with ParentChart.Canvas do
  begin
    if ((Align = ttaNear) and (Marks.Tail.Edge = tteBottom))
       or ((Align = ttaFar) and (Marks.Tail.Edge = tteLeft)) then
       result := Calculate3DPosition(x + margin, y - margin, zPos)
    else
    if ((Align = ttaFar) and (Marks.Tail.Edge = tteBottom))
       or ((Align = ttaFar) and (Marks.Tail.Edge = tteRight))  then
       result := Calculate3DPosition(x - margin, y - margin, zPos)
    else
    if ((Align = ttaNear) and (Marks.Tail.Edge = tteRight))
       or ((Align = ttaFar) and (Marks.Tail.Edge = tteTop)) then
       result := Calculate3DPosition(x - margin, y + margin, zPos)
    else
    if ((Align = ttaNear) and (Marks.Tail.Edge = tteTop)) then
       result := Calculate3DPosition(x + margin, y + margin, zPos)
    else //i.e. ((degrees >= 315) && (degrees < 360))
       result := Calculate3DPosition(x + margin, y + margin, zPos);
  end;
end;

Procedure TPieSeries.DrawMark( ValueIndex:Integer; Const St:String;
                               APosition:TSeriesMarkPosition);
var tmpXRadius : Integer;
    tmpYRadius : Integer;
    tmp        : Double;

    procedure DoCalcPos(const ALength:Single; out P:TPoint);
    begin
      AngleToPos(tmp,tmpXRadius+ALength,tmpYRadius+ALength,P.X,P.Y);
    end;

    procedure AntiOverlap;
    var A,B : TRect;
    begin
      if ValueIndex>FirstDisplayedIndex then
      begin
        A:=TSeriesMarksAccess(Marks).TotalBounds(ValueIndex,APosition);

        {$IFDEF TEEPIEDEBUG}
        ParentChart.Canvas.Brush.Style:=bsClear;
        ParentChart.Canvas.Rectangle(A,0);
        {$ENDIF}

        if Assigned(Marks.Positions[ValueIndex-1]) then
        begin
          B:=TSeriesMarksAccess(Marks).TotalBounds(ValueIndex-1,Marks.Positions[ValueIndex-1]);

          // Overlaps?
          if TeeRectIntersects(B,A) then
          begin
            if A.Top<CircleYCenter then
              APosition.Offset(TeePoint(0,A.Top-B.Top))
            else
              APosition.Offset(TeePoint(0,B.Top-A.Top));
          end;
        end;
      end;
    end;

var tmpXPos    : Integer;
    tmpYPos    : Integer;
    OldAngle   : Integer;
    OldArrow   : Boolean;
    OldAuto    : Boolean;
    tmpAngle   : Integer;
    tmpX       : TCoordinate;
    tmpY       : TCoordinate;
    tmpAlign : TTailAlign;
begin
  if not BelongsToOtherSlice(ValueIndex) then
  if PieMarks.EmptySlice or (MandatoryValueList.Value[ValueIndex]<>0) then
  begin
    CalcExplodedRadius(ValueIndex,tmpXRadius,tmpYRadius);

    // Verify Pie slice angles are calculated
    CheckAngles;

    tmp:=FAngles[ValueIndex].MidAngle;

    if (Count=1) and SameValue(tmp,Pi) then
       tmp:=0;

    if ParentChart.Canvas.SupportsFullRotation then
       Marks.ZPosition:=EndZ-SliceEndZ(ValueIndex)
    else
       Marks.ZPosition:=SliceEndZ(ValueIndex);

    OldArrow:=Marks.Callout.Arrow.Visible;

    if PieMarks.InsideSlice then
    begin
      Marks.Callout.Arrow.Visible:=False;

      AngleToPos(tmp, tmpXRadius*0.5, tmpYRadius*0.5, tmpXPos, tmpYPos);

      APosition.LeftTop.X:=tmpXPos-(APosition.Width div 2);
      APosition.LeftTop.Y:=tmpYPos-(APosition.Height div 2);
    end
    else
    begin
      with APosition do
      begin
        ArrowFix:=True;

        DoCalcPos(Marks.Callout.Length+Marks.Callout.Distance,ArrowTo);
        DoCalcPos(Marks.Callout.Distance,ArrowFrom);

        tmpX:=ArrowTo.X;
        tmpY:=ArrowTo.Y;

        if tmpX>FCircleXCenter then LeftTop.X:=tmpX
                               else LeftTop.X:=tmpX-Width;

        if tmpY>FCircleYCenter then LeftTop.Y:=tmpY
                               else LeftTop.Y:=tmpY-Height;

        if Self.PieMarks.VertCenter then
        begin
          tmpYPos:=Height div 2;

          if tmpY>FCircleYCenter then tmpY:=tmpY+tmpYPos
                                 else tmpY:=tmpY-tmpYPos;
        end;

        if Self.PieMarks.LegSize=0 then
        begin
          HasMid:=False;
          MidPoint.X:=0;
          MidPoint.Y:=0;
        end
        else
        begin
          HasMid:=True;

          if tmpX>FCircleXCenter then
          begin
            if tmpX-PieMarks.LegSize < ArrowFrom.X then
            begin
              MidPoint.X:=ArrowFrom.X;
              tmpX:=tmpX+PieMarks.LegSize;
              LeftTop.X:=tmpX;
            end
            else
              MidPoint.X:=tmpX-PieMarks.LegSize;
          end
          else
          begin
            if tmpX+PieMarks.LegSize > ArrowFrom.X then
            begin
              MidPoint.X:=ArrowFrom.X;
              tmpX:=ArrowFrom.X-PieMarks.LegSize;
              LeftTop.X:=tmpX-Width;
            end
            else
              MidPoint.X:=tmpX+PieMarks.LegSize;
          end;

          MidPoint.Y:=tmpY;
        end;

        ArrowTo.X:=tmpX;
        ArrowTo.Y:=tmpY;

        if Marks.Automatic.Move then
           AntiOverlap;
      end;
    end;

    OldAngle:=Marks.Angle;
    OldAuto:=Marks.AutoPosition;

    if Marks.Tail.Visible and FAutoMarkPointer then
    Begin
      tmpX:=APosition.ArrowFrom.X;
      tmpY:=APosition.ArrowFrom.Y;

      tmpAlign:=Marks.Tail.Align;

      Marks.Tail.Edge:=GetMarkQuadrant(tmp,tmpAlign);

      if Marks.Tail.CustomPoint then
         Marks.Tail.CustomPointPos:=GetTailEnd({$IFDEF FMX}System.Round{$ENDIF}(tmpX),
                                               {$IFDEF FMX}System.Round{$ENDIF}(tmpY),
                                               Marks.Tail.Margin,
                                               Marks.ZPosition,
                                               tmpAlign);
      Marks.Tail.Align:=tmpAlign;
    end;

    if PieMarks.Rotated then
    begin
      tmpAngle:=RotationAngle+Round(tmp*HalfDivPi);

      if PieMarks.RotateStyle=rsTangencial then
         tmpAngle:=tmpAngle+90;

      if (tmpAngle>=90) and (tmpAngle<=270) then
      begin
        tmpAngle:=tmpAngle+180;

        if tmpAngle>360 then
           tmpAngle:=tmpAngle-360;
      end;

      Marks.Angle:=tmpAngle;
      Marks.AutoPosition:=False;
    end;

    inherited;

    Marks.Angle:=OldAngle;
    Marks.AutoPosition:=OldAuto;

    if PieMarks.InsideSlice then
       Marks.Callout.Arrow.Visible:=OldArrow;
  end;
end;

Function TPieSeries.CompareSlice(A,B:Integer):Integer;

  Function GetAngleSlice(Index:Integer):Double;
  const HalfPi=Pi*0.5;

    function NormalizeAngle(const Angle:Double):Double;
    begin
      result:=Angle;

      if result<HalfPi then result:=HalfPi-result
      else
      if result<1.5*Pi then result:=result-HalfPi
      else
         result:=Pi-(result-1.5*Pi);
    end;

  const TwoPi=2.0*Pi;
  var tmpStart,
      tmpRange,
      tmpEnd    : Double;
  begin
    tmpStart:=IRotDegree+FAngles[Index].StartAngle;
    tmpEnd:=IRotDegree+FAngles[Index].EndAngle;

    tmpRange:=tmpEnd-tmpStart;

    tmpStart:=tmpStart+0.05*tmpRange;
    while tmpStart>TwoPi do tmpStart:=tmpStart-TwoPi;

    tmpEnd:=tmpEnd-0.05*tmpRange;
    while tmpEnd>TwoPi do tmpEnd:=tmpEnd-TwoPi;

    tmpStart:=NormalizeAngle(tmpStart);
    tmpEnd:=NormalizeAngle(tmpEnd);

    if (tmpStart>HalfPi) and (tmpEnd>HalfPi) then
      if tmpStart>tmpEnd then result:=tmpStart
                         else result:=tmpEnd
    else
      if tmpStart<tmpEnd then result:=tmpStart
                         else result:=tmpEnd;

    //Labels[Index]:=FormatFloat('#.##',tmpStart)+' '+FormatFloat('#.##',tmpEnd);
  end;

var tmpA : Double;
    tmpB : Double;
begin
  tmpA:=GetAngleSlice(ISortedSlice[A]);
  tmpB:=GetAngleSlice(ISortedSlice[B]);

  if tmpA<tmpB then result:=-1 else
  if tmpA>tmpB then result:= 1 else result:= 0;
end;

Procedure TPieSeries.SwapSlice(a,b:Integer);
begin
  SwapInteger(ISortedSlice[a],ISortedSlice[b]);
end;

procedure TPieSeries.DrawAllValues;
var
  tmpR : TRect;

  procedure DrawShadow(const AShadow:TTeeShadow);
  var t : Integer;
      tmpOffX,
      tmpOffY : Integer;
      tmpR2 : TRect;
  begin
    if AShadow.Visible then
       if IsExploded then
       begin
         for t:=0 to Count-1 do
         begin
           CalcExplodedOffset(t,tmpOffX,tmpOffY);

           tmpR2:=tmpR;

           if Pen.Visible then
              InflateRect(tmpR2,2*Pen.Width,2*Pen.Width);

           OffsetRect(tmpR2,tmpOffX,-tmpOffY);

           with FAngles[t] do
            AShadow.DrawEllipse(ParentChart.Canvas,
                 tmpR2,
                 DonutPercent,
                 (StartAngle+IRotDegree)*HalfDivPi,
                 (EndAngle+IRotDegree)*HalfDivPi);
         end;
       end
       else
          AShadow.DrawEllipse(ParentChart.Canvas,tmpR,DonutPercent,RotationAngle,RotationAngle+AngleSize);
  end;

  procedure DrawPieShadow;
  var tmpShadowZ : Integer;
      tmpBlend   : TTeeBlend;
  begin
    tmpR:=CircleRect;

    // OpenGL only
    if ParentChart.Canvas.SupportsFullRotation then
    begin
      tmpShadowZ:=EndZ+10;
    end
    else
    begin
      tmpShadowZ:=StartZ-10;
      tmpR:=ParentChart.Canvas.CalcRect3D(CircleRect,tmpShadowZ);
    end;

    FShadow.ZPosition:=tmpShadowZ;
    FEmboss.ZPosition:=tmpShadowZ;

    if Transparency>0 then
       tmpBlend:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transparency)
    else
       tmpBlend:=nil;

    DrawShadow(FShadow);
    DrawShadow(FEmboss);

    if Transparency>0 then
       ParentChart.Canvas.EndBlending(tmpBlend);
  end;

  procedure DrawOtherLegend;
  begin
    ILegend:=OtherSlice.Legend;
    try
      OtherSlice.Legend.DrawLegend;
    finally
      ILegend:=nil;
    end;
  end;

  procedure CalcSortedSlices(const ACount:Integer);
  var t : Integer;
  begin
    SetLength(ISortedSlice,ACount);

    for t:=FirstValueIndex to LastValueIndex do
        ISortedSlice[t-FirstValueIndex]:=t;

    TeeSort(0,ACount-1,CompareSlice,SwapSlice);
  end;

  { calc biggest exploded index }
  function CalcMaxExplodedIndex:Integer;
  var t,
      MaxExploded : Integer;
  begin
    result:=-1;

    if FExplodedSlice.Count>0 then
    begin
      MaxExploded:=0;

      for t:=FirstValueIndex to LastValueIndex do
          if FExplodedSlice[t]>MaxExploded then
          begin
            MaxExploded:=Round(FExplodedSlice[t]);
            result:=t;
          end;
    end;
  end;

  procedure ExplodeCircleRect(const MaxExplodedIndex:Integer);
  var tmpOffX : Integer;
      tmpOffY : Integer;
  begin
    CalcExplodedOffset(MaxExplodedIndex,tmpOffX,tmpOffY);
    {$IFDEF D16}System.Types.{$ENDIF}InflateRect(FCircleRect,-Abs(tmpOffX) div 2,-Abs(tmpOffY) div 2);

    SetCircleRect(FCircleRect);
  end;

  procedure DrawSortedSlices(const ACount:Integer);
  var tmpForcedExploded: Boolean;
      tmpOldExploded   : Boolean;
      t : Integer;
  begin
    CalcSortedSlices(ACount);

    tmpForcedExploded:=False;
    tmpOldExploded:=IsExploded;

    for t:=0 to ACount-1 do
    begin
      if (AngleSize<>360) then     //TV52015865
         if (ISortedSlice[t]=FirstValueIndex) or (ISortedSlice[t]=LastValueIndex) then
         begin
           IsExploded:=True;
           tmpForcedExploded:=True;
         end;

      DrawValue(ISortedSlice[t]);

      if tmpForcedExploded then
         IsExploded:=tmpOldExploded;
    end;

    ISortedSlice:=nil;
  end;

var tmpCount         : Integer;
    MaxExplodedIndex : Integer;
begin
  DrawFrame;

  if FExplodeBiggest>0 then
     CalcExplodeBiggest;

  tmpCount:=(LastValueIndex-FirstValueIndex)+1;

  MaxExplodedIndex:=CalcMaxExplodedIndex;

  { calc each slice angles }
  CheckAngles;

  { adjust circle rectangle }
  IsExploded:=(MaxExplodedIndex<>-1) or (FSliceHeights.Count>0);

  if MaxExplodedIndex<>-1 then
     ExplodeCircleRect(MaxExplodedIndex);

  if (FShadow.Visible or FEmboss.Visible) and (FMultiPie<>mpConcentric) then
     DrawPieShadow;

  { exploded slices for 3D drawing, sort order... }
  if ParentChart.View3D and (not ParentChart.Canvas.SupportsFullRotation) then
     DrawSortedSlices(tmpCount)
  else
     inherited;

  if Assigned(OtherSlice.FLegend) and OtherSlice.Legend.Visible then
     DrawOtherLegend;
end;

procedure TPieSeries.CheckAngles;
begin
  if Length(FAngles)=0 then
     CalcAngles;
end;

Procedure TPieSeries.DrawPie(ValueIndex:Integer);
const
  HalfDivPi=180.0/Pi;

var tmpOffX : Integer;
    tmpOffY : Integer;
    tmpGradient : TChartGradient;
    tmpVisual : TVisualBlock;
    tmpBlend  : TTeeBlend;
    tmpR      : TRect;
Begin
  CalcExplodedOffset(ValueIndex,tmpOffX,tmpOffY);

  // Special case, when this Pie series is not the only series in the chart,
  // we should check here if "FAngles" array is still empty (not yet calculated).
  //
  // Explanation:
  // By default, CalcAngles is called at Pie "DrawAllValues".
  // DrawAllValues is only called when Pie is the only series in a chart.
  CheckAngles;

  with FAngles[ValueIndex] do
  with ParentChart,Canvas do { Draw pie slice }
  if View3D then
  begin
    if Self.CircleGradient.Visible then
       tmpGradient:=Self.CircleGradient
    else
       tmpGradient:=nil;

    tmpVisual:=Visuals[ValueIndex];

    if Assigned(tmpVisual) then
    begin
      // ? how to pass angles + donutpercent to block?
    end
    else
    begin
      Pie3D( FCircleXCenter+tmpOffX,
             FCircleYCenter-tmpOffY,
             FXRadius,FYRadius,
             StartZ,
             SliceEndZ(ValueIndex),
             StartAngle+IRotDegree,EndAngle+IRotDegree,
             FDark3D,
             IsExploded,
             FDonutPercent,
             tmpGradient,
             FBevel.Percent,
             FEdgeStyle,
             Transparency,
             FBevel.Bright,
             FBevel.UseBorder,
             IHideSides)
    end
  end
  else
  begin
    // Global Pie rectangle:
    tmpR.Left:=FCircleXCenter+tmpOffX-FXRadius;
    tmpR.Top:=FCircleYCenter-tmpOffY-FYRadius;
    tmpR.Right:=FCircleXCenter+tmpOffX+FXRadius;
    tmpR.Bottom:=FCircleYCenter-tmpOffY+FYRadius;

    if Self.Transparency>0 then
       tmpBlend:=BeginBlending(tmpR,Self.Transparency)
    else
       tmpBlend:=nil;

    if FDonutPercent>0 then
    begin
      if PieValues.Value[ValueIndex]<>0 then // TV52012419 (obsolete fix?)
         Donut( FCircleXCenter+tmpOffX,
                FCircleYCenter-tmpOffY,
                FXRadius,FYRadius,
                StartAngle+IRotDegree,EndAngle+IRotDegree,
                FDonutPercent)
    end
    else
      Pie(tmpR,(IRotDegree+StartAngle)*HalfDivPi,(IRotDegree+EndAngle)*HalfDivPi);

    if Bevel.Percent<>0 then
    begin
      Brush.Color:=ChangeColor(Brush.Color,FBevel.Bright);

      if not Bevel.UseBorder then
         Pen.Style:=psClear;

      Donut( FCircleXCenter+tmpOffX,
             FCircleYCenter-tmpOffY,
             FXRadius,FYRadius,
             StartAngle+IRotDegree,EndAngle+IRotDegree,
             100-Bevel.Percent)
    end;

    if Self.Transparency>0 then
       EndBlending(tmpBlend);
  end;
end;

Procedure TPieSeries.CalcExplodedOffset( ValueIndex:Integer;
                                         out OffsetX,OffsetY:Integer);
var tmpExp : Double;
    tmpSin : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmpCos : {$IFDEF D15}Double{$ELSE}Extended{$ENDIF};
    tmp    : Double;
begin
  OffsetX:=0;
  OffsetY:=0;

  if IsExploded and (FExplodedSlice.Count>0) then
  begin
    tmpExp:=FExplodedSlice[ValueIndex];

    // Apply exploded % to radius
    if tmpExp>0 then
    begin
      with FAngles[ValueIndex] do
      {if ParentChart.Canvas.SupportsFullRotation then
         tmp:=MidAngle+(Pi*FAngleSize/45.0)+Pi
      else}
         tmp:=MidAngle;

      SinCos(tmp+IRotDegree,tmpSin,tmpCos);
      tmpExp:=tmpExp*0.01;
      OffsetX:=Round(FXRadius*tmpExp*tmpCos);
      OffsetY:=Round(FYRadius*tmpExp*tmpSin);
    end;
  end;
end;

Procedure TPieSeries.CalcExplodeBiggest;
var tmp : Integer;
    t   : Integer;
begin
  // Explode if there are at least 2 slices:
  if Count>1 then
  begin
    if Assigned(ParentChart) and (ParentChart.MaxPointsPerPage>0) then
    begin
      // Explode if there are at least 2 slices:
      if (FirstValueIndex<>-1) and ((LastValueIndex-FirstValueIndex)>0) then
      begin
        tmp:=FirstValueIndex;

        for t:=FirstValueIndex+1 to LastValueIndex do
            if YValues.Value[t]>YValues.Value[tmp] then
               tmp:=t;
      end
      else tmp:=-1;
    end
    else
       with YValues do tmp:=Locate(MaxValue);

    if tmp<>-1 then
       FExplodedSlice[tmp]:=FExplodeBiggest;
  end;
end;

Procedure TPieSeries.SetAngleSize(Value:Integer);
begin
  SetIntegerProperty(FAngleSize,Value);
end;

procedure TPieSeries.DrawMarks;
var tmpMarksTail : TMarksTail;
begin
  tmpMarksTail:=TMarksTail.Create(Self);
  try
    if Marks.Tail.AutoAlign then
    Begin
      FAutoMarkPointer:=True;

      tmpMarksTail.Assign(Marks.Tail);
      tmpMarksTail.CustomPoint:=Marks.Tail.CustomPoint;
    end;

    inherited;

    if FAutoMarkPointer then
    begin
      FAutoMarkPointer:=False;
      Marks.Tail:=tmpMarksTail;
    end;
  finally
    tmpMarksTail.Free;
  end;
end;

function TPieSeries.GetMarkQuadrant(const Angle:Double; var Align:TTailAlign):TTailEdge;
var degrees : Double;
Begin
  degrees := RadToDeg(Angle) + RotationAngle;

  if degrees > 360 then
     degrees := degrees - 360;

  if (degrees >= 0) and (degrees < 45) then
  Begin
    Align := ttaFar;
    result := tteLeft;  //tailLeftBottom
  end
  else
  if (degrees >= 45) and (degrees < 90) then
  Begin
    Align := ttaNear;
    result := tteBottom;
  end
  else
  if (degrees >= 90) and (degrees < 135) then
  Begin
    Align := ttaFar;
    result := tteBottom;  //tailBottomRight
  end
  else
  if (degrees >= 135) and (degrees < 180) then
  Begin
    Align := ttaFar;
    result := tteRight;   //tailRightBottom
  end
  else
  if (degrees >= 180) and (degrees < 225) then
  Begin
    Align := ttaNear;
    result := tteRight; //tailRightTop
  end
  else
  if (degrees >= 225) and (degrees < 270) then
  Begin
    Align := ttaFar;
    result := tteTop; //tailTopRight
  end
  else
  if (degrees >= 270) and (degrees < 315) then
  Begin
    Align := ttaNear;
    result := tteTop; //tailTopLeft
  end
  else //i.e. ((degrees >= 315) && (degrees < 360))
  Begin
    Align := ttaNear;
    result := tteLeft; //tailLeftTop
  end
end;

function TPieSeries.GetAutoMarkPosition:Boolean;
Begin
  result:=Marks.AutoPosition;
end;

Procedure TPieSeries.SetAutoMarkPosition(Value:Boolean);
Begin
  Marks.AutoPosition:=Value;
end;

function TPieSeries.GetBevelPercent:Integer;
begin
  result:=FBevel.FPercent;
end;

Procedure TPieSeries.SetBevel(const Value:TPieBevel);
begin
  FBevel.Assign(Value);
end;

procedure TPieSeries.SetBevelPercent(Value: Integer);
begin
  SetIntegerProperty(FBevel.FPercent,Value);
end;

procedure TPieSeries.SetDonutPercent(Value:Integer);
begin
  SetIntegerProperty(FDonutPercent,Value);
end;

procedure TPieSeries.SetEdgeStyle(Value: TEdgeStyle);
begin
  if FEdgeStyle<>Value then
  begin
    FEdgeStyle:=Value;
    Repaint;
  end;
end;

procedure TPieSeries.SetExplodeBiggest(Value:Integer);
begin
  SetIntegerProperty(FExplodeBiggest,Value);
  CalcExplodeBiggest;
end;

procedure TPieSeries.SetGradientBright(Value:Integer);
begin
  SetIntegerProperty(FGradientBright,Value);

  if GradientBright=0 then
     Gradient.StartColor:={$IFDEF FMX}TAlphaColors.White{$ELSE}clWhite{$ENDIF};
end;

procedure TPieSeries.SetMultiPie(const Value:TMultiPie);
begin
  if FMultiPie<>Value then
  begin
    FMultiPie:=Value;
    Repaint;
  end;
end;

procedure TPieSeries.SetOtherSlice(Value:TPieOtherSlice);
begin
  FOtherSlice.Assign(Value);
end;

Procedure TPieSeries.DrawValue(ValueIndex:Integer);
var tmpColor : TColor;
    tmpGradient: TTeeGradient;
Begin
  if (CircleWidth>4) and (CircleHeight>4) then
  if not BelongsToOtherSlice(ValueIndex) then
  begin
    Brush.Style:=SliceBrushStyle(ValueIndex);

    if ParentChart.Monochrome then tmpColor:={$IFDEF FMX}TAlphaColors.Black{$ELSE}clBlack{$ENDIF}
                              else tmpColor:=ValueColor[ValueIndex];

    { Set slice back color }
    if tmpColor=clNone then
       ParentChart.Canvas.Brush.Style:=bsClear
    else
    begin
      ParentChart.SetBrushCanvas(tmpColor,Brush,CalcCircleBackColor);

      if CircleGradient.Visible then
      begin
        tmpGradient:=TTeeGradient.Create(nil);
        try
          //SetGradientColor(tmpGradient,tmpColor);
          ParentChart.Canvas.Gradient.Assign(tmpGradient);
        finally
          tmpGradient.Free;
        end;

        //SetGradientColor(ParentChart.Canvas.Gradient,tmpColor); // TV52014800
      end
      else
        // Prevent bad brush (ie: when changing from a Bar series with Gradient to a Pie:
        ParentChart.Canvas.Gradient.Visible:=False;
    end;

    PreparePiePen(ValueIndex);

    DrawPie(ValueIndex);
  end;
end;

procedure TPieSeries.SetUsePatterns(Value:Boolean);
Begin
  SetBooleanProperty(FUsePatterns,Value);
end;

class Function TPieSeries.GetEditorClass:String;
Begin
  result:='TPieSeriesEditor'; { <-- dont translate ! }
end;

Function TPieSeries.GetPieValues:TChartValueList;
Begin
  result:=YValues;
end;

Function TPieSeries.GetPiePen:TChartPen;
begin
  result:=Pen;
end;

Procedure TPieSeries.SetDark3D(Value:Boolean);
Begin
  SetBooleanProperty(FDark3D,Value);
End;

Procedure TPieSeries.SetPieMarks(const Value:TPieMarks);
begin
  FPieMarks.Assign(Value);
end;

Procedure TPieSeries.SetPieValues(const Value:TChartValueList);
Begin
  SetYValues(Value);
end;

Function TPieSeries.MaxXValue:TChartValue;
Begin
  result:=GetHorizAxis.Maximum;
End;

Function TPieSeries.MinXValue:TChartValue;
Begin
  result:=GetHorizAxis.Minimum;
End;

Function TPieSeries.MaxYValue:TChartValue;
Begin
  result:=GetVertAxis.Maximum;
End;

Function TPieSeries.MinYValue:TChartValue;
Begin
  result:=GetVertAxis.Minimum;
End;

Procedure TPieSeries.DisableRotation;
begin
  With ParentChart.View3DOptions do
  begin
    Orthogonal:=False;
    Rotation:=0;
    Elevation:=305;
  end;
end;

Procedure TPieSeries.PrepareForGallery;
Begin
  inherited;

  FillSampleValues(8);
  ParentChart.Chart3DPercent:=75;
  Marks.Callout.Length:=0;
  Marks.DrawEvery:=1;

  {$IFNDEF FMX}
  Gradient.Visible:=True;
  {$ENDIF}

  DisableRotation;
  ColorEachPoint:=True;
end;

Procedure TPieSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TPieSeries then
  with TPieSeries(Source) do
  begin
    Self.FAngleSize     :=FAngleSize;
    Self.Bevel          :=FBevel;
    Self.FDark3D        :=FDark3D;
    Self.FDarkPen       :=FDarkPen;
    Self.FDonutPercent  :=FDonutPercent;
    Self.FExplodeBiggest:=FExplodeBiggest;
    Self.FExplodedSlice.Assign(FExplodedSlice);
    Self.FMultiPie      :=FMultiPie;
    Self.FSliceHeights.Assign(FSliceHeights);
    Self.FUsePatterns   :=FUsePatterns;
    Self.OtherSlice     :=FOtherSlice;
    Self.PieMarks.Assign(FPieMarks);
  end;

  inherited;

  ColorEachPoint:=True;
end;

Function TPieSeries.AddPie( Const AValue:Double;
                            Const ALabel:String; AColor:TColor):Integer;
begin
  result:=Add(AValue,ALabel,AColor);
end;

procedure TPieSeries.RemoveOtherSlice;
var t : Integer;
begin
  { remove "other" slice, if exists... }
  for t:=0 to Count-1 do
  if XValues.Value[t]=TeePieOtherFlag then
  begin
    Inc(IUpdating);
    Delete(t);
    Dec(IUpdating);
    Break;
  end;
end;

procedure TPieSeries.WriteData(Stream: TStream);
begin
  RemoveOtherSlice;
  inherited;
end;

Procedure TPieSeries.CheckOrder;
begin
  XValues.Order:=loAscending;  // reset X order back to ascending
  Repaint;
  // do not call inherited. Simply repaint.
  // Pie series will already sort values at DoBeforeDrawChart method.
end;

procedure TPieSeries.Clear;
begin
  inherited;
  FAngles:=nil;
end;

procedure TPieSeries.DoBeforeDrawChart;

  // Try to find a custom mark for "Other" slice, and return a copy
  function OtherMarkCustom:TSeriesMarkPosition;
  var t   : Integer;
      tmp : TSeriesMarkPosition;
  begin
    result:=nil;

    for t:=0 to Count-1 do
    if XValues.Value[t]=TeePieOtherFlag then
    begin
      tmp:=Marks.Positions.Position[t];

      if Assigned(tmp) and tmp.Custom then
      begin
        result:=TSeriesMarkPosition.Create;
        result.Assign(tmp);
      end;

      Break;
    end;
  end;

var t            : Integer;
    tmp          : Double;
    tmpValue     : Double;
    tmpHasOther  : Boolean;
    tmpOtherMark : TSeriesMarkPosition;
begin
  inherited;

  tmpOtherMark:=OtherMarkCustom;

  { re-order values }
  With PieValues do
       if Order<>loNone then Sort;

  RemoveOtherSlice;

  // Force re-creating all Angles
  FAngles:=nil;

  // Reset X order, eliminating all slices that could belong to "Other"
  // slice in a previous repaint.
  Inc(IUpdating);
  XValues.FillSequence;
  Dec(IUpdating);

  { calc "Other" slice... }
  if (FOtherSlice.Style<>poNone) and (MandatoryValueList.TotalABS>0) then
  Begin
    tmpHasOther:=False;
    tmpValue:=0;

    for t:=0 to Count-1 do
    begin
      tmp:=Abs(YValues.Value[t]);

      if FOtherSlice.Style=poBelowPercent then
         tmp:=tmp*100.0/YValues.TotalAbs;

      if tmp<FOtherSlice.Value then
      begin
        tmpValue:=tmpValue+Abs(YValues.Value[t]);

        XValues.Value[t]:=TeePieBelongsToOther; { <-- belongs to "other" }

        tmpHasOther:=True;
      end;
    end;

    { Add "Other" slice }
    if tmpHasOther then
    begin
      Inc(IUpdating);
      t:=AddXY(TeePieOtherFlag,tmpValue,FOtherSlice.Text,FOtherSlice.Color);
      Dec(IUpdating);

      With YValues do
           TotalABS:=TotalABS-tmpValue; { reset Y total }

      // Reset custom mark for "Other" slice
      if Assigned(tmpOtherMark) then
         Marks.Positions.Position[t]:=tmpOtherMark;
    end;
  end;

  FreeAndNil(tmpOtherMark);
end;

procedure TPieSeries.DoAfterDrawValues;
begin
  FDonutPercent:=IOldDonut;
  ParentChart.ChartRect:=IOldChartRect;
  inherited;
end;

procedure TPieSeries.DoBeforeDrawValues;

  procedure GuessRectangle;

    function PieIndex:Integer;
    var t : Integer;
        tmp : TChartSeries;
    begin
      result:=0;

      for t:=0 to ParentChart.SeriesCount-1 do
      begin
        tmp:=ParentChart[t];

        if tmp=Self then break
        else
        if tmp.Active and SameClass(tmp) then
           Inc(result);
      end;
    end;

  var tmpR : TRect;
      tmpIndex : Integer;
      tmpHeight,
      tmpWidth,
      tmpW,
      tmpH : Integer;
      tmpRow,
      tmpCols,
      tmpRows  : Integer;
      tmpCount : Integer;
  begin
    tmpCount:=PieCount;

    if tmpCount>1 then
    begin
      tmpR:=ParentChart.ChartRect;
      tmpIndex:=PieIndex;
      tmpW:={$IFDEF FMX}Round{$ENDIF}(tmpR.Right-tmpR.Left);
      tmpH:={$IFDEF FMX}Round{$ENDIF}(tmpR.Bottom-tmpR.Top);

      tmpCols:=Round(Sqrt(tmpCount));

      tmpWidth:=(tmpW div tmpCols);

      tmpR.Left:=tmpR.Left+(tmpIndex mod tmpCols)*tmpWidth;
      tmpR.Right:=tmpR.Left+Round(0.9*tmpWidth);

      tmpRows:=Round(0.5+Sqrt(tmpCount));
      tmpRow:=tmpIndex div tmpCols;

      tmpHeight:=(tmpH div tmpRows);

      tmpR.Top:=tmpR.Top+tmpRow*tmpHeight;
      tmpR.Bottom:=tmpR.Top+Round(0.9*tmpHeight);  // 0.9 = 10% margin

      ParentChart.ChartRect:=ParentChart.Canvas.CalcRect3D(tmpR,0);
    end;
  end;

  procedure PrepareConcentric;

    // Returns True when ASeries is visible, its a Pie, and is "concentric"
    function IsConcentric(const ASeries:TChartSeries):Boolean;
    begin
      result:=ASeries.Active and (ASeries is TPieSeries) and (TPieSeries(ASeries).MultiPie=mpConcentric);
    end;

    // Returns number of visible Pie series in "concentric" mode:
    function CountConcentric:Integer;
    var t: Integer;
    begin
      result:=0;

      for t:=0 to ParentChart.SeriesCount-1 do
        if IsConcentric(ParentChart[t]) then
           Inc(result);
    end;

  var t : Integer;
      tmpIndex : Integer;
      tmpCount : Integer;
      Previous,
      tmp : TChartSeries;
      tmpFactor : Single;
  begin
    tmpCount:=CountConcentric;

    // Calculate our "index" in the visible concentric Pies, and the "previous" pie series:
    Previous:=nil;
    tmpIndex:=0;

    for t:=0 to ParentChart.SeriesCount-1 do
    begin
      tmp:=ParentChart[t];

      if tmp=Self then
         break
      else
      if IsConcentric(tmp) then
      begin
        Previous:=tmp;
        Inc(tmpIndex);
      end;
    end;

    // Dont paint slice "donut fronts":
    IHideSides:=True;

    // Calculate concentric radius:
    tmpFactor:=(tmpIndex+1)*(100 div tmpCount)*0.01;

    FXRadius:=Round(tmpFactor*XRadius);
    FYRadius:=Round(tmpFactor*YRadius);

    // And interior "donut" hole:
    if Assigned(Previous) then
       DonutPercent:=Round(100*TPieSeries(Previous).FXRadius/FXRadius)
    else
       DonutPercent:=0;
  end;

begin
  IAdjustCircleForMarks:=not PieMarks.InsideSlice;

  IOldChartRect:=ParentChart.ChartRect;
  IOldDonut:=FDonutPercent;

  if MultiPie=mpConcentric then
  begin
    inherited;

    PrepareConcentric;
  end
  else
  begin
    IHideSides:=False;

    if MultiPie=mpAutomatic then
       GuessRectangle;

    inherited;
  end;
end;

procedure TPieSeries.SwapValueIndex(a,b:Integer);

  Procedure ExchangeSliceList(const AList:TSliceValueList);
  begin
    With AList do
    if Count>0 then
    begin
      while Self.Count>Count do
            Add({$IFDEF D18}0{$ELSE}nil{$ENDIF});

      Exchange(a,b);
    end;
  end;

begin
  inherited;

  ExchangeSliceList(FExplodedSlice);
  ExchangeSliceList(FSliceHeights);
end;

procedure TPieSeries.Delete(ValueIndex:Integer);
begin
  inherited;
  if FExplodedSlice.Count>ValueIndex then
     FExplodedSlice.Delete(ValueIndex);

  if FSliceHeights.Count>ValueIndex then
     FSliceHeights.Delete(ValueIndex);
end;

class Procedure TPieSeries.SetSubGallery(ASeries:TChartSeries; Index:Integer);
var tmp : TPieSeries;
begin
  With TPieSeries(ASeries) do
  Case Index of
    1: begin
         UsePatterns:=True;
         Gradient.Visible:=False;
       end;
    2: ExplodeBiggest:=30;
    3: With Shadow do
       begin
         HorizSize:=10;
         VertSize:=10;
       end;
    4: begin
         Marks.Visible:=True;
         Clear;
         Add(30,'A'); // Do not localize
         Add(70,'B'); // Do not localize
       end;
    5: AngleSize:=180;
    6: Pen.Hide;
    7: DarkPen:=32;
    8: begin
         MultiPie:=mpConcentric;
         tmp:=(CreateNewSubSeries(ASeries) as TPieSeries);
         tmp.MultiPie:=mpConcentric;
         tmp.Marks.Hide;
         tmp.FillSampleValues(ASeries.Count);
       end;
  end;
end;

class Procedure TPieSeries.CreateSubGallery(AddSubChart:TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Patterns);
  AddSubChart(TeeMsg_Exploded);
  AddSubChart(TeeMsg_Shadow);
  AddSubChart(TeeMsg_Marks);
  AddSubChart(TeeMsg_SemiPie);
  AddSubChart(TeeMsg_NoBorder);
  AddSubChart(TeeMsg_DarkPen);
  AddSubChart(TeeMsg_Concentric);
end;

procedure TPieSeries.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;

  if not (csDestroying in ComponentState) then
  begin
    if Assigned(OtherSlice) and Assigned(OtherSlice.FLegend) then
       OtherSlice.Legend.ParentChart:=Value;

    if Assigned(FFrame) then
       FFrame.ParentChart:=Value;
  end;
end;

procedure TPieSeries.SetDarkPen(const Value: Integer);
begin
  SetIntegerProperty(FDarkPen,Value);
end;

Procedure TPieSeries.CalcFirstLastVisibleIndex;
begin
  if FOtherSlice.Style=poNone then
     CalcFirstLastPage(FFirstVisibleIndex,FLastVisibleIndex)
  else
     FFirstVisibleIndex:=-1;

  if FirstValueIndex=-1 then inherited;
end;

procedure TPieSeries.CalcSelectionPos(ValueIndex:Integer; out X,Y:Integer);
var tmpX : Integer;
    tmpY : Integer;
begin
  if (Count>ValueIndex) and (Length(FAngles)>ValueIndex) then
  begin
    CalcExplodedRadius(ValueIndex,tmpX,tmpY);
    AngleToPos(FAngles[ValueIndex].MidAngle,tmpX,tmpY,X,Y);
  end
  else
     inherited CalcSelectionPos(ValueIndex,X,Y);
end;

Procedure TPieSeries.CalcZOrder;
begin
//  inherited;  <-- do not call inherited here !  TV520100404
  IZOrder:=-1;
end;

// Do not modify the "CircleRect" if there's another Pie series in Chart,
procedure TPieSeries.CheckOtherSeriesMarks;
begin // Dont call inherited.
end;

{ TFastLineSeries }

type
  TPenAccess=class(TTeePen);
  THoverAccess=class(TTeeHover);

Constructor TFastLineSeries.Create(AOwner: TComponent);
Begin
  inherited;

  {$IFDEF TEEOCX}
  FExpandAxis:=25;
  {$ENDIF}

  // Set default Pen color, to force using the Series.Color:
  TPenAccess(Pen).InitColor(clTeeColor);

  TPenAccess(Pen).InitEndStyle({$IFDEF D16}TPenEndStyle.{$ENDIF}esFlat);

  AllowSinglePoint:=False;
  DrawBetweenPoints:=True;
  IMandatoryPen:=True;

  FAutoRepaint:=True;
  FDrawAll:=True;
  FIgnoreNulls:=True;
  TreatNulls:=tnIgnore;

  Selected.Hover.Visible:=False;
  THoverAccess(Selected.Hover).DefaultVisible:=False;
end;

Destructor TFastLineSeries.Destroy;
begin
  IPoints:=nil;
  inherited;
end;

Procedure TFastLineSeries.CalcPosition(ValueIndex:Integer; out x,y:Integer);
begin
  X:=GetHorizAxis.CalcXPosValue(XValues.Value[ValueIndex]);
  Y:=GetVertAxis.CalcYPosValue(YValues.Value[ValueIndex]);
end;

Procedure TFastLineSeries.NotifyNewValue(Sender:TChartSeries; ValueIndex:Integer);
{$IFNDEF TEEOCX}
var tmp,
    tmpX,
    tmpY   : Integer;

    {$IFNDEF FMX}
    tmpDC : TTeeCanvasHandle;
    {$ENDIF}

{$ENDIF}
begin
  if AutoRepaint then inherited
  {$IFNDEF TEEOCX}
  else
  begin
    if ValueIndex=0 then tmp:=0 else tmp:=Pred(ValueIndex);

    CalcPosition(tmp,tmpX,tmpY);

    if (not DrawAllPoints) and (OldX<>tmpX) then
    begin
      OldX:=tmpX;
      OldY:=tmpY;
    end;

    With ParentChart,Canvas do
    begin
      {$IFNDEF FMX}
      {$IFDEF MSWINDOWS}
      if FastPen then
      begin
        tmpDC:=Handle;
        SelectObject(tmpDC,DCPEN);
        TeeSetDCPenColor(tmpDC,LinePen.Color);
      end
      else
      {$ENDIF}
      {$ENDIF}
        AssignVisiblePen(LinePen);

      if View3D then MoveTo3D(OldX,OldY,MiddleZ)
                else MoveTo(OldX,OldY);
    end;

    DrawValue(ValueIndex);
  end;
  {$ENDIF}
end;

class Function TFastLineSeries.GetEditorClass:String;
Begin
  result:='TFastLineSeriesEditor'; { <-- dont translate ! }
End;

procedure TFastLineSeries.PrepareCanvas;
Begin
  With ParentChart.Canvas do
  begin
    if Self.LinePen.Color=clTeeColor then
       AssignVisiblePenColor(Self.LinePen,Self.Color)
    else
       AssignVisiblePen(Self.LinePen);

    Brush.Style:=bsClear;
    BackMode:=cbmTransparent;
  end;
end;

procedure TFastLineSeries.DoMove(const X,Y:Integer);
begin
  With ParentChart.Canvas do
  if ParentChart.View3D then MoveTo3D(X,Y,MiddleZ)
                        else MoveTo(X,Y);
end;

// Fast Polyline drawing can only be done in 2D, when there are no null values,
// and not in stairs mode.
function TFastLineSeries.ShouldDrawAsPolyline:Boolean;
begin
  result:=FDrawAll and (FDrawStyle=flAll) and (TreatNulls=tnIgnore) and
          (not Stairs) and (Assigned(ParentChart) and (not ParentChart.View3D));
end;

function TFastLineSeries.UseFastLine:Boolean;
begin
  // OpenGL Canvas does not support polyline mode and fastline mode at the same time:
  result:=FFastPen and
          ((not ParentChart.Canvas.SupportsFullRotation) or (not ShouldDrawAsPolyline));
end;

function TFastLineSeries.CanBeforeParallel:Boolean;
begin
  result:=ShouldDrawAsPolyline;
end;

{$IFDEF FASTLINEFLOAT}
type
  TAxisAccess=class(TChartAxis);
{$ENDIF}

procedure TFastLineSeries.DoBeforeParallel;
var AOffset : Integer;

  // This is a procedure (instead of inlined), to improve CPU register allocation:
  {$IFDEF FASTLINEFLOAT}
  procedure CalcLoopX(const AValues:TChartValues; const P:TPointFloatArray);
  var t : Integer;
      tmp : Integer;
      tmpMin, tmpRange : Single;
  begin
    tmpMin:=TAxisAccess(GetHorizAxis).IMinimum;
    tmpRange:=TAxisAccess(GetHorizAxis).IAxisSizeRange;

    if GetHorizAxis.Inverted then
    begin
      tmp:=GetHorizAxis.IEndPos;

      for t:=Succ(AOffset) to LastValueIndex do
          P[t-AOffset].X:=tmp-(AValues[t]-tmpMin)*tmpRange;
    end
    else
    begin
      tmp:=GetHorizAxis.IStartPos;

      for t:=Succ(AOffset) to LastValueIndex do
          P[t-AOffset].X:=tmp+(AValues[t]-tmpMin)*tmpRange;
    end;
  end;

  // This is a procedure (instead of inlined), to improve CPU register allocation:
  procedure CalcLoopY(const AValues:TChartValues; const P:TPointFloatArray);
  var t : Integer;
      tmp : Integer;
      tmpMin, tmpRange : Single;
  begin
    tmpMin:=TAxisAccess(GetVertAxis).IMinimum;
    tmpRange:=TAxisAccess(GetVertAxis).IAxisSizeRange;

    if GetVertAxis.Inverted then
    begin
      tmp:=GetVertAxis.IStartPos;

      for t:=Succ(AOffset) to LastValueIndex do
          P[t-AOffset].Y:=tmp+(AValues[t]-tmpMin)*tmpRange;
    end
    else
    begin
      tmp:=GetVertAxis.IEndPos;

      for t:=Succ(AOffset) to LastValueIndex do
          P[t-AOffset].Y:=tmp-(AValues[t]-tmpMin)*tmpRange;
    end;
  end;

  {$ELSE}

  procedure CalcLoopX(const ICalc:TAxisCalcPos;
                      const AValues:TChartValues;
                      const P:TPointArray);
  var t : Integer;
  begin
    for t:=Succ(AOffset) to LastValueIndex do
        P[t-AOffset].X:=ICalc(AValues[t]);
  end;

  // This is a procedure (instead of inlined), to improve CPU register allocation:
  procedure CalcLoopY(const ICalc:TAxisCalcPos;
                      const AValues:TChartValues;
                      const P:TPointArray);
  var t : Integer;
  begin
    // Loop candidate to multi-thread:
    for t:=Succ(AOffset) to LastValueIndex do
        P[t-AOffset].Y:=ICalc(AValues[t]);
  end;
  {$ENDIF}

var tmpL : Integer;
    tmp  : Integer;
    tmpCount : Integer;
begin
  CalcFirstLastVisibleIndex;

  tmp:=FirstValueIndex;

  if tmp>0 then CalcPosition(Pred(tmp),OldX,OldY)
           else CalcPosition(0,OldX,OldY);

  if ShouldDrawAsPolyline then
  begin
    AOffset:=FirstValueIndex;

    if AOffset>0 then
       Dec(AOffset);

    tmpCount:=LastValueIndex-AOffset+1;
    tmpL:=Length(IPoints);

    if tmpL<>tmpCount then
       SetLength(IPoints,tmpCount);

    // First point (or pre-first if FirstValueIndex > 0)
    IPoints[0].X:=OldX;
    IPoints[0].Y:=OldY;

    if (tmpL=0) or (not ReuseXValues) then
       CalcLoopX({$IFNDEF FASTLINEFLOAT}GetHorizAxis.CalcXPosValue,{$ENDIF}XValues.Value,IPoints);

    if (tmpL=0) or (not ReuseYValues) then
       CalcLoopY({$IFNDEF FASTLINEFLOAT}GetVertAxis.CalcYPosValue,{$ENDIF}YValues.Value,IPoints);
  end;
end;

procedure TFastLineSeries.DrawAllValues;

  function CalcGradient(out R:{$IFDEF LCL}TeCanvas.{$ENDIF}TRectF):TTeeGradient;
  var tmpSize : Integer;
      tmpR : TRect;
  begin
    if ParentChart.Canvas.Pen.IsGradientVisible then
    begin
      result:=ParentChart.Canvas.Pen.Fill.Gradient;

      tmpSize:={$IFDEF FMX}Round{$ENDIF}(ParentChart.Canvas.Pen.Width {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF})+1;

      R:=result.CustomTargetRectangle;

      tmpR:=TeeRect(GetHorizAxis.CalcXPosValue(XValues.MinValue)-tmpSize,
                    GetVertAxis.CalcYPosValue(YValues.MaxValue)-tmpSize,
                    GetHorizAxis.CalcXPosValue(XValues.MaxValue)+tmpSize,
                    GetVertAxis.CalcYPosValue(YValues.MinValue)+tmpSize);

      result.CustomTargetRectangle:=TeeRect(tmpR);
    end
    else
      result:=nil;
  end;

  procedure DrawAsPolyline;
  var tmpGradient : TTeeGradient;
      OldR : {$IFDEF LCL}TeCanvas.{$ENDIF}TRectF;
  begin
    // GDI+ and FMX:
    tmpGradient:=CalcGradient(OldR);

    {$IFDEF FASTLINEFLOAT}
    ParentChart.Canvas.PolylineFloat(IPoints);
    {$ELSE}
    if ParentChart.View3D then
       ParentChart.Canvas.Polyline(IPoints,MiddleZ)
    else
       ParentChart.Canvas.Polyline(IPoints);
    {$ENDIF}

    if Assigned(tmpGradient) then
       tmpGradient.CustomTargetRectangle:=OldR;
  end;

  procedure DrawAsSegments;
  var t   : Integer;
      tmp : Integer;
  begin
    tmp:=FirstValueIndex;

    if tmp>0 then
       DrawValue(tmp);

    for t:=Succ(tmp) to LastValueIndex do
        DrawValue(t);
  end;

var tmpTransp : Boolean;
    tmpBlend : TTeeBlend;
    tmpUsePolyLine : Boolean;
    tmpUseFastLine : Boolean;
begin
  IDrawingAllValues:=True;
  tmpTransp:=(Transparency>0) and ParentChart.Canvas.SupportsAlpha;

  if tmpTransp then
     tmpBlend:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transparency)
  else
     tmpBlend:=nil;

  PrepareCanvas;

  tmpUsePolyLine:=ShouldDrawAsPolyline;

  if not tmpUsePolyLine then
  begin
    DoMove(OldX,OldY);

    if not DrawAllPoints then
    begin
      IMin:=OldY;
      IMax:=OldY;
      ISameX:=False;
    end;
  end;

  tmpUseFastLine:=UseFastLine;

  if tmpUseFastLine then
     TCanvasAccess(ParentChart.Canvas).SetFastLine(True);

  // Draw all line segments as a single Polyline instead of multiple LineTo calls.
  // This allows filling line interior with gradient colors (VCL with GDI+, and FireMonkey)
  if tmpUsePolyLine then
     DrawAsPolyline
  else
     DrawAsSegments;

  if tmpTransp then
     ParentChart.Canvas.EndBlending(tmpBlend);

  if tmpUseFastLine then
     TCanvasAccess(ParentChart.Canvas).SetFastLine(False);

  IDrawingAllValues:=False;
end;

procedure TFastLineSeries.DrawValue(ValueIndex:Integer);

  function ShouldDrawPoint(var AX,AY:Integer):Boolean;

    function IsNewPixelPosition:Boolean;
    begin
      result:=True;

      if AX=OldX then
         if (not FDrawAll and (FDAPStyle=daFirst)) or (AY=OldY) then
            result:=False;
    end;

  begin
    result:=(FColors=nil) or (not IsNull(ValueIndex));

    if result then
    begin
      result:=(valueIndex=0) or (IsNewPixelPosition);
      if (not result and (not DrawAllPoints) and (DrawAllPointsStyle=daMinMax)) then
         if ((valueIndex>0) and (not IsNull(valueIndex-1))) then
            result:=true;

      if (IsNull(valueIndex-1) and
         (
           (TreatNulls=tnDontPaint) or
           (
             (TreatNulls=tnSkip) and (valueIndex-1=FirstDisplayedIndex)
           )
         ) or
         (valueIndex=0)) then
      begin
        result:=False;
        DoMove(AX, AY);

        //TV52016561
        if Stairs then
        begin
          OldX:=AX;
          OldY:=AY;
        end;
      end;
    end
    else
    begin
      if TreatNulls=tnIgnore then
         result:=IsNewPixelPosition or ((ValueIndex>0) and (not IsNull(ValueIndex-1)))
      else
      if TreatNulls=tnSkip then
      begin
        AX:=OldX;
        AY:=OldY;
      end
      else
      if not FDrawAll and (TreatNulls=tnDontPaint) then
      begin
        case DrawAllPointsStyle of
          daFirst: DoMove(AX,AY);
          daMinMax:
          begin
            OldX:=AX;
            OldY:=AY;
          end;
        end;
      end;
    end;
  end;

  procedure DoDrawPoint(AX, AY:Integer);
  begin
    if ParentChart.View3D then
    with ParentChart.Canvas do
      begin
        if Stairs then
           if InvertedStairs then LineTo3D(OldX,AY,MiddleZ)
                             else LineTo3D(AX,OldY,MiddleZ);

        LineTo3D(AX,AY,MiddleZ);
      end
    else
    if UseFastLine then
      with TCanvasAccess(ParentChart.Canvas) do
      begin
        if not IDrawingAllValues then
           SetFastLine(True);

        if Stairs then
           if InvertedStairs then FastLineTo(OldX,AY)
                             else FastLineTo(AX,OldY);

        FastLineTo(AX,AY);

        if not IDrawingAllValues then
           SetFastLine(False);
      end
    else
    with ParentChart.Canvas do
      begin
        if Stairs then
           if InvertedStairs then LineTo(OldX,AY)
                             else LineTo(AX,OldY);

        LineTo(AX,AY);
      end;

    OldX:=AX;
    OldY:=AY;
  end;

var X : Integer;
    Y : Integer;
begin
  CalcPosition(ValueIndex, X, Y);

  // For max speed, draw directly if we ignore null points, and we can DrawAll:
  if (TreatNulls=tnIgnore) and FDrawAll then
     DoDrawPoint(X, Y)
  else
  if ShouldDrawPoint(X,Y) then
  begin
    if not FDrawAll and (FDAPStyle=daMinMax) then
    begin
      if X=OldX then
      begin
        if Y < IMin then IMin:=Y;
        if Y > IMax then IMax:=Y;
        ISameX:=true;

        if ValueIndex = LastDisplayedIndex then
        begin
          if GetVertAxis.Inverted then
          begin
            DoDrawPoint(OldX, IMax);
            DoDrawPoint(OldX, IMin);
          end
          else
            DoDrawPoint(OldX, IMin);
            DoDrawPoint(OldX, IMax);
          begin
          end;
        end
        else
          DoDrawPoint(X, Y);
      end
      else
      begin
        if ISameX then
        begin
          if GetVertAxis.Inverted then
          begin
            DoDrawPoint(OldX, IMax);
            DoDrawPoint(OldX, IMin);
          end
          else
          begin
            DoDrawPoint(OldX, IMin);
            DoDrawPoint(OldX, IMax);
          end;
        end;

        DoDrawPoint(X, Y);

        ISameX:=false;
        IMin:=Y;
        IMax:=Y;
      end;
    end
    else
      DoDrawPoint(X, Y);
  end
  else
  begin
    if (not FDrawAll) and (FDAPStyle=daMinMax) then
    begin
      if ValueIndex<>FirstDisplayedIndex then
         DoMove(X, Y);

      IMin:=Y;
      IMax:=Y;
    end;
  end;
end;

// When changing the SeriesColor, set LinePen color
Procedure TFastLineSeries.SetSeriesColor(AColor:TColor);
begin
  inherited;
  LinePen.Color:=AColor;
end;

Procedure TFastLineSeries.DrawLegendShape(ValueIndex:Integer; Const Rect:TRect);
begin
  with Rect{$IFDEF FMX}.Round{$ENDIF} do
       ParentChart.Canvas.DoHorizLine(Left,Right,(Top+Bottom) div 2);
end;

Procedure TFastLineSeries.Assign(Source:TPersistent);
begin
  if Source is TFastLineSeries then
  with TFastLineSeries(Source) do
  begin
    Self.FAutoRepaint:=AutoRepaint;
    Self.FClickTolerance:=ClickTolerance;
    Self.FDAPStyle:=FDAPStyle;
    Self.FDrawAll:=DrawAllPoints;
    Self.FDrawStyle:=FDrawStyle;
    Self.FFastPen:=FastPen;
    Self.FIgnoreNulls:=IgnoreNulls;
    Self.FInvertedStairs:=InvertedStairs;
    Self.FStairs:=Stairs;
  end;

  inherited;
end;

Procedure TFastLineSeries.AssignFormatting(Source:TChartSeries);
begin
  inherited;

  Pen.Visible:=True; // Pen should be always visible

  // Pen.Color should not be copied, so set it to clTeeColor
  Pen.Color:=clTeeColor;
end;

procedure TFastLineSeries.Clear;
begin
  inherited;
  if (ParentChart <> nil) and (ParentChart.Canvas <> nil) then
    With ParentChart,Canvas do
    begin
        OldX := GetVertAxis.PosAxis; //LeftAxis.PosAxis;
        OldY := GetHorizAxis.PosAxis; //BottomAxis.PosAxis;
        if View3D then MoveTo3D(OldX,OldY,MiddleZ)
                  else MoveTo(OldX,OldY);
    end;
end;

Function TFastLineSeries.Clicked(x,y:Integer):Integer;
var t    : Integer;
    OldX : Integer;
    OldY : Integer;
    tmpX : Integer;
    tmpY : Integer;
    P    : TPoint;
begin
  result:=TeeNoPointClicked;

  if (FirstValueIndex>-1) and (LastValueIndex>-1) then
  begin
    if Assigned(ParentChart) then
       ParentChart.Canvas.Calculate2DPosition(X,Y,MiddleZ);

    OldX:=0;
    OldY:=0;
    P.X:=X;
    P.Y:=Y;

    for t:=FirstValueIndex to LastValueIndex do
    begin
      tmpX:=CalcXPos(t);
      tmpY:=CalcYPos(t);

      if (tmpX=X) and (tmpY=Y) then { clicked right on point }
      begin
        result:=t;
        break;
      end
      else
      if t>FirstValueIndex then
         if Stairs then
         begin
           if PointInLine(P,tmpX,tmpY,tmpX,OldY,ClickTolerance) or
              PointInLine(P,tmpX,OldY,OldX,OldY,ClickTolerance) then
           begin
             result:=t-1;
             break;
           end;
         end
         else
         if PointInLine(P,tmpX,tmpY,OldX,OldY,ClickTolerance) then
         begin
           result:=t-1;
           break;
         end;

      OldX:=tmpX;
      OldY:=tmpY;
    end;
  end;
end;

Procedure TFastLineSeries.DrawMark( ValueIndex:Integer; Const St:String;
                                    APosition:TSeriesMarkPosition);
Begin
  Marks.ApplyArrowLength(APosition);
  inherited;
end;

class Procedure TFastLineSeries.CreateSubGallery(AddSubChart:TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Marks);
  AddSubChart(TeeMsg_Dotted);
  AddSubChart(TeeMsg_Stairs);
end;

class Procedure TFastLineSeries.SetSubGallery(ASeries:TChartSeries; Index:Integer);
begin
  with TFastLineSeries(ASeries) do
  case Index of
     1: Marks.Visible:=True;
     2: LinePen.SmallDots:=True;
     3: Stairs:=True;
  end;
end;

// When changing the series Pen, set the SeriesColor to pen color
procedure TFastLineSeries.SetPen(const Value: TChartPen);
begin
  inherited;
  SeriesColor:=LinePen.Color;
end;

procedure TFastLineSeries.SetDAPStyle(const Value: TDrawAllPointsStyle);
begin
  if Value<>FDAPStyle then
  begin
    FDAPStyle:=Value;
    Repaint;
  end;
end;

procedure TFastLineSeries.SetDrawAll(const Value: Boolean);
begin
  SetBooleanProperty(FDrawAll,Value);
end;

Procedure TFastLineSeries.SetDrawStyle(const Value:TFastLineDrawStyle);
begin
  if FDrawStyle<>Value then
  begin
    FDrawStyle:=Value;
    Repaint;
  end;
end;

procedure TFastLineSeries.SetFastPen(const Value: Boolean);
begin
  if FFastPen<>Value then
  begin
    FFastPen:=Value;

    {$IFNDEF FMX}
    {$IFDEF MSWINDOWS}
    if Assigned(@TeeSetDCPenColor) then
       DCPEN:=GetStockObject(DC_PEN);
    {$ENDIF}
    {$ENDIF}

    Repaint;
  end;
end;

procedure TFastLineSeries.SetIgnoreNulls(const Value: Boolean);
begin
  SetBooleanProperty(FIgnoreNulls,Value);
end;

procedure TFastLineSeries.SetInvertedStairs(const Value: Boolean);
begin
  SetBooleanProperty(FInvertedStairs,Value);
end;

procedure TFastLineSeries.SetStairs(const Value: Boolean);
begin
  SetBooleanProperty(FStairs,Value);
end;

procedure TFastLineSeries.CalcFirstLastVisibleIndex;

  // Fixes: TV52015898 and TV52016511
  function GetLastYMaxIndex(ValueIndex: Integer): Integer;
  var pixelXPos : Integer;
  begin
    result:=ValueIndex;
    pixelXPos:=GetHorizAxis.CalcXPosValue(XValues.Value[result]);

    while result>0 do
      if GetHorizAxis.CalcXPosValue(XValues.Value[result-1]) <> pixelXPos then
         break
      else
         Dec(result);
  end;

begin
  inherited;

  if (not FDrawAll) and (FFirstVisibleIndex = FLastVisibleIndex) then
      if FFirstVisibleIndex>0 then
         FFirstVisibleIndex:=GetLastYMaxIndex(FFirstVisibleIndex-1)
      else
         FFirstVisibleIndex:=-1;
end;

// Increases and decreases horizontal margins with rounded Pen.Width
procedure TFastLineSeries.CalcHorizMargins(var LeftMargin,
  RightMargin: Integer);
var tmp : Integer;
begin
  inherited;

  tmp:={$IFDEF FMX}Round{$ENDIF}(Pen.Width-((Pen.Width-2) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}));

  Inc(LeftMargin,tmp);
  Inc(RightMargin,tmp);
end;

// Increases and decreases vertical margins with rounded Pen.Width
procedure TFastLineSeries.CalcVerticalMargins(var TopMargin,
  BottomMargin: Integer);
var tmp : Integer;
begin
  inherited;

  tmp:={$IFDEF FMX}Round{$ENDIF}(Pen.Width-((Pen.Width-2) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF}));

  if tmp<>0 then
  begin
    if MaxYValue>=GetVertAxis.Maximum then
       Inc(TopMargin,tmp);

    if MinYValue<=GetVertAxis.Minimum then
       Inc(BottomMargin,tmp);
  end;
  
//  if Marks.Visible then
//     TopMargin:=Math.Max(TopMargin,Marks.Callout.Length+Marks.Callout.Distance);
end;

procedure TFastLineSeries.PrepareLegendCanvas(ValueIndex: Integer;
  var BackColor: TColor; var BrushStyle: {$IFDEF FMX}TBrushKind{$ELSE}TBrushStyle{$ENDIF});
begin
  PrepareCanvas;
end;

Procedure RegisterTeeStandardSeries;
begin
  RegisterTeeSeries(TLineSeries,     @TeeMsg_GalleryLine);
  RegisterTeeSeries(TBarSeries,      @TeeMsg_GalleryBar);
  RegisterTeeSeries(THorizBarSeries, @TeeMsg_GalleryHorizBar);
  RegisterTeeSeries(TAreaSeries,     @TeeMsg_GalleryArea);
  RegisterTeeSeries(TPointSeries,    @TeeMsg_GalleryPoint);
  RegisterTeeSeries(TPieSeries,      @TeeMsg_GalleryPie,      @TeeMsg_GalleryStandard,1);

  RegisterTeeSeries(TFastLineSeries, @TeeMsg_GalleryFastLine);

  {$IFNDEF TEELITEEMB}
  RegisterTeeSeries(THorizLineSeries,@TeeMsg_GalleryHorizLine,@TeeMsg_GalleryStandard,1);
  RegisterTeeSeries(THorizAreaSeries,@TeeMsg_GalleryHorizArea,@TeeMsg_GalleryStandard,1);
  {$ENDIF}
end;

end.
