{*********************************************}
{ TeeChart Delphi Component Library           }
{ Polar and Radar Series Components           }
{ Copyright (c) 1995-2020 by Steema Software  }
{ All rights reserved                         }
{*********************************************}
unit TeePolar;
{$I TeeDefs.inc}

interface

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

     SysUtils, Classes,

     {$IFDEF D16}
     System.UITypes,
     {$ENDIF}

     {$IFDEF FMX}
     FMX.Types, System.Types,

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

     {$ELSE}
     Graphics, Controls, Forms,
     {$ENDIF}

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

     {$IFDEF LCL}
     LCLType,
     {$ENDIF}

     TeCanvas, TeeProcs, TeEngine, Chart, Series
     {$ENDIF}
     ;

type
  TCustomCircledSeries=class(TCircledSeries)
  private
    FCirclePen        : TChartPen;
    FColorEachLine    : Boolean;
    FDrawStyle        : TCustomSeriesDrawStyle;
    FOnGetPointerStyle: TOnGetPointerStyle;
    FPointer          : TSeriesPointer;
    FPointerBehind    : Boolean;

    procedure DoDrawAll;
    Procedure SetCirclePen(const Value:TChartPen);
    procedure SetColorEachLine(Value:Boolean);
    procedure SetDrawStyle(const Value:TCustomSeriesDrawStyle);
    Procedure SetPointer(const Value:TSeriesPointer);
    Procedure SetPointerBehind(const Value:Boolean);
  protected
    AxisXRadius : Integer;
    AxisYRadius : Integer;

    Procedure AssignFormatting(Source:TChartSeries); override;
    procedure CalcRadius; override;
    function CalcValueColor(ValueIndex:Integer):TColor;
    function DoGetPointerStyle(ValueIndex:Integer):TSeriesPointerStyle;
    procedure DrawAllValues; override;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    function ShouldCloseCircle:Boolean; virtual;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Function LegendItemColor(LegendIndex:Integer):TColor; override;

    property CirclePen:TChartPen read FCirclePen write SetCirclePen;
    property ColorEachLine:Boolean read FColorEachLine write SetColorEachLine default True;
    property Pointer:TSeriesPointer read FPointer write SetPointer;
    property PointerBehind:Boolean read FPointerBehind write SetPointerBehind default False;
    property DrawStyle:TCustomSeriesDrawStyle read FDrawStyle write SetDrawStyle default dsSegments;

    { events }
    property OnGetPointerStyle:TOnGetPointerStyle read FOnGetPointerStyle write FOnGetPointerStyle;
  end;

  TCustomPolarSeries=class;

  TPolarLabels=class(TTeeShape)
  private
    FAntiOverlap: Boolean;
    FClockWise : Boolean;
    FCurved    : Boolean;
    FInside    : Boolean;
    FMargin    : Integer;
    FRotated   : Boolean;

    IPolar : TCustomPolarSeries;

    Procedure SetAntiOverlap(Value:Boolean);
    procedure SetInside(Value: Boolean);
    procedure SetClockWise(Value: Boolean);
    procedure SetCurved(Value: Boolean);
    procedure SetMargin(Value: Integer);
    Procedure SetRotated(Value:Boolean);
  public
    Constructor Create(const Polar:TCustomPolarSeries);

    procedure Assign(Source:TPersistent); override;
  published
    property AntiOverlap:Boolean read FAntiOverlap write SetAntiOverlap default True;
    property Brush;
    property ClockWise:Boolean read FClockWise write SetClockWise default False;
    property Curved:Boolean read FCurved write SetCurved default False;
    property Inside:Boolean read FInside write SetInside default False;
    property Margin:Integer read FMargin write SetMargin default 3; // 3%
    property Pen;
    property Rotated:Boolean read FRotated write SetRotated default False;
    property Transparent default True;
  end;

  TOnGetPolarLabel=procedure(Sender:TCustomPolarSeries; const Angle:Double;
                             Index:Integer; var Text:String) of object;

  TCustomPolarSeries=class(TCustomCircledSeries, ITeeScreenToPoint)
  private
    FAngleLabels      : TPolarLabels;
    FCloseCircle      : Boolean;
    FOnGetCircleLabel : TOnGetPolarLabel;

    { Private declarations }
    IMaxValuesCount   : Integer;
    IShouldDrawLabels : Boolean;

    Procedure CalcXYPos( ValueIndex:Integer;
                         ARadius:Integer; out X,Y:Integer);
    Function DrawAxesBehind:Boolean;
    Procedure DrawAxis;
    Procedure FillTriangle(Const A,B:TPoint; Z:Integer);
    Function GetAngleIncrement:Double;
    Function GetRadiusIncrement:Double;
    Function GetRadiusValues:TChartValueList;
    Function IsRadiusStored:Boolean;
    Procedure SetAngleIncrement(Const Value:Double);
    procedure SetAngleLabels(const Value:TPolarLabels);
    Procedure SetCloseCircle(Value:Boolean);
    Procedure SetRadiusIncrement(Const Value:Double);
    Procedure SetRadiusValues(const Value:TChartValueList);

    // Deprecated, see property AngleLabels:TPolarLabels
    function GetCircleLabels:Boolean;
    procedure SetCircleLabels(Value:Boolean);
    function GetCircleLabelsFont:TTeeFont;
    procedure SetCircleLabelsFont(const Value:TTeeFont);
    function GetCircleLabelsInside:Boolean;
    procedure SetCircleLabelsInside(Value:Boolean);
    function GetCircleLabelsRot:Boolean;
    procedure SetLabelsRotated(Value:Boolean);
    function GetClockWiseLabels:Boolean;
    procedure SetClockWiseLabels(Value:Boolean);
    function GetLabelsMargin:Integer;
    procedure SetLabelsMargin(Value:Integer);

  protected
    { Protected declarations }
    OldX : Integer;
    OldY : Integer;

    Procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;
    Procedure AssignFormatting(Source:TChartSeries); override;
    Procedure CalcHorizMargins(var LeftMargin,RightMargin:Integer); override;
    Procedure CalcVerticalMargins(var TopMargin,BottomMargin:Integer); override;
    Procedure CalcXYPosition(const XValue,YValue:TChartValue; ARadius:Integer; out X,Y:Integer); overload;
    Procedure CalcXYPosition(const XValue,YValue:TChartValue; ARadius:Integer; Axis:TChartAxis; out X,Y:Integer); overload;
    Procedure CalcXYRadius(Const Value:Double; out X,Y:Integer);
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    Procedure DoAfterDrawValues; override;
    Procedure DoBeforeDrawValues; override;

    Procedure DrawLegendShape(ValueIndex:Integer; Const Rect:TRect); override;
    Procedure DrawMark( ValueIndex:Integer; Const St:String;
                        APosition:TSeriesMarkPosition); override;
    Procedure DrawPolarCircle(HalfWidth,HalfHeight,Z:Integer); virtual;
    procedure DrawValue(ValueIndex:Integer); override;

    Function GetAngleValue(ValueIndex:Integer):TChartValue; virtual;
    Function GetAngleValues:TChartValueList; virtual;
    Function  GetCircleLabel(Const Angle:Double; Index:Integer):String; virtual;
    class Function  GetEditorClass:String; override;
    procedure InternalDrawValue(ValueIndex,X,Y:Integer); virtual;
    procedure LinePrepareCanvas(ValueIndex:Integer);
    Procedure PrepareForGallery; override;

    // ITeeScreenToPoint
    procedure ScreenToPoint(X,Y,ValueIndex:Integer; ChangeX,ChangeY:Boolean);

    Procedure SetAngleValues(Value:TChartValueList); virtual;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    Procedure SetSeriesColor(AColor:TColor); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
    function ShouldCloseCircle:Boolean; override;
    Procedure SetAngleValue(Index:Integer; Const Value:TChartValue); virtual;
  public
    { Public declarations }
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Procedure Assign(Source:TPersistent); override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Function CalcYPos(ValueIndex:Integer):Integer; override;
    Function Clicked(x,y:Integer):Integer; override;
    Procedure DrawRing(Const Value:Double; Z:Integer);
    Function DrawValuesForward:Boolean; override;
    Procedure DrawZone(Const Min,Max:Double; Z:Integer);

    { to be Published declarations }
    property AngleIncrement:Double read GetAngleIncrement write SetAngleIncrement;
    property AngleValues:TChartValueList read GetAngleValues write SetAngleValues;
    property Brush;

    {$IFDEF TEEOCX}
    property CircleGradient;
    {$ENDIF}

    // Deprecated. Replaced with property: AngleLabels:TPolarLabels
    property CircleLabels:Boolean read GetCircleLabels write SetCircleLabels stored False;
    property CircleLabelsFont:TTeeFont read GetCircleLabelsFont write SetCircleLabelsFont stored False;
    property CircleLabelsInside:Boolean read GetCircleLabelsInside write SetCircleLabelsInside stored False;
    property CircleLabelsRotated:Boolean read GetCircleLabelsRot write SetLabelsRotated stored False;
    property ClockWiseLabels:Boolean read GetClockWiseLabels write SetClockWiseLabels stored False;
    property LabelsMargin:Integer read GetLabelsMargin write SetLabelsMargin stored False;
    //

    property AngleLabels:TPolarLabels read FAngleLabels write SetAngleLabels;
    property CirclePen;
    property CloseCircle:Boolean read FCloseCircle
                                 write SetCloseCircle default True;
    property ColorEachLine;
    property DrawStyle;
    property Pen;
    property Pointer;
    property PointerBehind;
    property RadiusIncrement:Double read GetRadiusIncrement write SetRadiusIncrement;
    property RadiusValues:TChartValueList read GetRadiusValues write SetRadiusValues
                                          stored IsRadiusStored;
    property Transparency;
    property TreatNulls;

    { events }
    property OnGetCircleLabel:TOnGetPolarLabel read FOnGetCircleLabel write FOnGetCircleLabel;
    property OnGetPointerStyle;
  end;

  TPolarSeries = class(TCustomPolarSeries, ITeeScreenToPoint)
  private
    FAngleValues:TChartValueList;
  protected
    Procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;
    Procedure DoAfterDrawValues; override;
    Procedure DoBeforeDrawValues; override;

    Function GetAngleValue(ValueIndex:Integer):TChartValue; override;
    Function GetAngleValues:TChartValueList; override;
    Procedure SetAngleValue(Index:Integer; Const Value:TChartValue); override;
    Procedure SetAngleValues(Value:TChartValueList); override;

    // ITeeScreenToPoint
    procedure ScreenToPoint(X,Y,ValueIndex:Integer; ChangeX,ChangeY:Boolean);
  public
    Constructor Create(AOwner: TComponent); override;

    Function AddNull(Const ALabel:String=''):Integer; override;
    Function AddNullXY(Const X,Y:TChartValue; Const ALabel:String=''):Integer; override;

    Function AddPolar( Const Angle,Value:Double;
                       Const ALabel:String='';
                       AColor:TColor=clTeeColor):Integer; virtual;
    Function UseAxis:Boolean; override;
    Function CanZoomScroll:Boolean; override;
    Function CalcYPosValue(Const Value:Double):Integer; override;
    Function XScreenToValue(ScreenPos:Integer):Double; override;
    Function YScreenToValue(ScreenPos:Integer):Double; override;
    Function CalcYPos(ValueIndex:Integer):Integer; override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;

    property AngleValue[Index:Integer]:TChartValue read GetAngleValue write SetAngleValue;
  published
    { Published declarations }
    property Active;
    property ColorEachPoint;
    property HorizAxis;
    property SeriesColor;
    property VertAxis;

    property AngleIncrement;
    property AngleValues;
    property Brush;
    property CircleBackColor;
    property CircleBrush;
    property CircleGradient;

    property AngleLabels;

    // Deprecated. See AngleLabels property
    property CircleLabels;
    property CircleLabelsFont;
    property CircleLabelsInside;
    property CircleLabelsRotated;
    property ClockWiseLabels;
    property LabelsMargin;

    property Labels;
    property CirclePen;
    property CloseCircle;
    property DrawStyle;
    property Pen;
    property Pointer;
    property PointerBehind;
    property RadiusIncrement;
    property RadiusValues;
    property RotationAngle;
    property Transparency;
    property TreatNulls;

    { events }
    property OnGetCircleLabel;
    property OnGetPointerStyle;
  end;

  TRadarSeries=class(TCustomPolarSeries, ITeeScreenToPoint)
  protected
    Procedure AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False); override;
    Procedure DoBeforeDrawChart; override;
    Function GetCircleLabel(Const Angle:Double; Index:Integer):String; override;
    Function GetxValue(ValueIndex:Integer):TChartValue; override;
    Procedure PrepareForGallery; override;
  public
    Function NumSampleValues:Integer; override;
  published
    { Published declarations }
    property Active;
    property ColorEachPoint;
    property HorizAxis;
    property SeriesColor;
    property VertAxis;

    property Brush;
    property CircleBackColor;
    property CircleGradient;

    property AngleLabels;

    // Deprecated. See AngleLabels property
    property CircleLabels;
    property CircleLabelsFont;
    property CircleLabelsRotated;

    property DrawStyle;
    property LabelsMargin;

    property CirclePen;
    property CloseCircle;
    property Labels;
    property Pen;
    property Pointer;
    property PointerBehind;
    property RadiusIncrement;
    property RadiusValues;
    property Transparency;
    property TreatNulls;

    { events }
    property OnGetCircleLabel;
    property OnGetPointerStyle;
  end;

  TPolarBarSeries=class(TPolarSeries)
  protected
    procedure InternalDrawValue(ValueIndex,X,Y:Integer); override;
  end;

implementation

Uses Math,
     {$IFNDEF FMX}
     {$IFDEF D6}
     Types,
     {$ENDIF}
     {$ENDIF}

     {$IFDEF FMX}
     {$IFDEF D17}
     System.UIConsts,
     {$ENDIF}
     FMXTee.Constants, FMXTee.ProConstants
     {$ELSE}
     TeeProCo, TeeConst
     {$ENDIF}
     ;


{ TCustomCircledSeries }

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

  FPointer:=TSeriesPointer.Create(Self);
  FCirclePen:=CreateChartPen;

  FColorEachLine:=True;

  Brush.Style:=bsClear;
  Brush.Color:=clTeeColor; // <-- Always after setting Style to bsClear
end;

Destructor TCustomCircledSeries.Destroy;
begin
  FCirclePen.Free;
  FreeAndNil(FPointer);
  inherited;
end;

Procedure TCustomCircledSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TCustomCircledSeries then
  With TCustomCircledSeries(Source) do
  begin
    Self.CirclePen    := FCirclePen;
    Self.FColorEachLine:=FColorEachLine;
    Self.FDrawStyle   := FDrawStyle;
    Self.Pointer      := FPointer;
    Self.FPointerBehind:=FPointerBehind;
  end;

  inherited;

  if Brush.Style=bsClear then
     Pen.Visible:=True; // <-- Always visible
end;

procedure TCustomCircledSeries.SetCirclePen(const Value: TChartPen);
begin
  FCirclePen.Assign(Value);
end;

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

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

Procedure TCustomCircledSeries.SetPointer(const Value:TSeriesPointer);
Begin
  FPointer.Assign(Value);
end;

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

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

procedure TCustomCircledSeries.DoDrawAll;
var t : Integer;
    L : Integer;
    P : TPointArray;
begin
  L:=0;

  for t:=FirstDisplayedIndex to LastDisplayedIndex do
  if IsPainted(t) then
  begin
    SetLength(P,L+1);
    P[L].X:=CalcXPos(t);
    P[L].Y:=CalcYPos(t);
    Inc(L);
  end;

  if L>0 then
  begin
    if Pen.Color=clTeeColor then
       ParentChart.Canvas.AssignVisiblePenColor(Pen,SeriesColor)
    else
       ParentChart.Canvas.AssignVisiblePen(Pen);

    if Brush.Style=bsClear then
       ParentChart.Canvas.Brush.Style:=bsClear
    else
    if Brush.Color=clTeeColor then
       ParentChart.Canvas.AssignBrush(Brush,SeriesColor)
    else
       ParentChart.Canvas.AssignBrush(Brush,Brush.Color);

    if DrawStyle=dsAll then
    begin
      if Brush.Style=bsClear then
         if ParentChart.View3D then
            ParentChart.Canvas.Polyline(P,MiddleZ)
         else
            ParentChart.Canvas.Polyline(P)
      else
      if ParentChart.View3D then
         ParentChart.Canvas.PolygonWithZ(P,MiddleZ)
      else
         ParentChart.Canvas.Polygon(P);

    end
    else
       ParentChart.Canvas.Curve(P,0.5,ShouldCloseCircle,MiddleZ);

    P:=nil;
  end;
end;

procedure TCustomCircledSeries.DrawAllValues;

  procedure DoDrawPointers(Behind:Boolean);
  var t : Integer;
      tmpColor : TColor;
  begin
    if Pointer.Visible and (Behind=PointerBehind) then
       for t:=FirstDisplayedIndex to LastDisplayedIndex do
         if IsPainted(t) then
         begin
           tmpColor:=CalcValueColor(t);

           if FPointer.Color<>clTeeColor then
              tmpColor:=FPointer.Color;

           FPointer.PrepareCanvas(ParentChart.Canvas,tmpColor);

           PrepareHoverPen(t);

           Pointer.Draw(CalcXPos(t), CalcYPos(t), tmpColor, DoGetPointerStyle(t));
         end;
  end;

var tmpBlend : TTeeBlend;
    tmpTransp : Boolean;
begin
  // Only for GDI+ and canvas classes than have Alpha per-pixel:
  tmpTransp:=(Transparency>0) and ParentChart.Canvas.SupportsAlpha;

  if tmpTransp then
     tmpBlend:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transparency)
  else
     tmpBlend:=nil;

  DoDrawPointers(True);

  if ((not ColorEachPoint) or (not ColorEachLine)) and (DrawStyle<>dsSegments) and
     ((not ParentChart.View3D) or ParentChart.View3DOptions.Orthogonal) then
     DoDrawAll
  else
     inherited;

  DoDrawPointers(False);

  if tmpTransp then
     ParentChart.Canvas.EndBlending(tmpBlend);
end;

function TCustomCircledSeries.CalcValueColor(ValueIndex:Integer):TColor;
begin
  result:=ValueColor[ValueIndex];

  if (result=clNone) and (TreatNulls=tnIgnore) then
     if ColorEachPoint then
        result:=GetDefaultColor(ValueIndex)
     else
        result:=Color;
end;

Procedure TCustomCircledSeries.SetParentChart(Const Value:TCustomAxisPanel);
Begin
  inherited;

  if Assigned(FPointer) then
     Pointer.ParentChart:=Value;

  if Assigned(ParentChart) then
     if (csDesigning in ComponentState) and (not (csLoading in ComponentState)) then
        ParentChart.View3D:=False;
end;

function TCustomCircledSeries.ShouldCloseCircle:Boolean;
begin
  result:=False;
end;

procedure TCustomCircledSeries.CalcRadius;
var tmp : Integer;
begin
  inherited;

  tmp:=CirclePen.HalfWidth;

  AxisXRadius:=XRadius-tmp;
  AxisYRadius:=YRadius-tmp;

  if Pointer.Visible and Pointer.InflateMargins then
  begin
    Dec(AxisXRadius,Pointer.HorizSize);
    Dec(AxisYRadius,Pointer.VertSize);
  end;
end;

{ TPolarLabels }

Constructor TPolarLabels.Create(const Polar:TCustomPolarSeries);
begin
  inherited Create(nil);
  IPolar:=Polar;

  DefaultTransparent:=True;
  Transparent:=True;

  FAntiOverlap:=True;
  FMargin:=3; // %
end;

procedure TPolarLabels.Assign(Source:TPersistent);
begin
  if Source is TPolarLabels then
  with TPolarLabels(Source) do
  begin
    Self.FAntiOverlap:=FAntiOverlap;
    Self.FCurved:=FCurved;
    Self.FInside:=FInside;
    Self.FRotated:=FRotated;
    Self.FMargin:=FMargin;
    Self.FClockWise:=FClockWise;
  end;

  inherited;
end;

Procedure TPolarLabels.SetAntiOverlap(Value:Boolean);
begin
  IPolar.SetBooleanProperty(FAntiOverlap,Value);
end;

procedure TPolarLabels.SetInside(Value: Boolean);
begin
  IPolar.SetBooleanProperty(FInside,Value);
end;

procedure TPolarLabels.SetMargin(Value: Integer);
begin
  IPolar.SetIntegerProperty(FMargin,Value);
end;

Procedure TPolarLabels.SetRotated(Value:Boolean);
begin
  IPolar.SetBooleanProperty(FRotated,Value);
end;

procedure TPolarLabels.SetCurved(Value:Boolean);
begin
  IPolar.SetBooleanProperty(FCurved,Value);
end;

procedure TPolarLabels.SetClockWise(Value:Boolean);

  procedure ModifyData;
  var t : Integer;
  begin
    with IPolar do
    for t:=0 to AngleValues.Count-1 do
        AngleValues[t]:=360-AngleValues[t];
  end;

begin
  if FClockWise<>Value then
  begin
    FClockWise:=Value;
    ModifyData;
    IPolar.CanvasChanged(Self);
  end;
end;

Function TCustomCircledSeries.LegendItemColor(LegendIndex:Integer):TColor;
begin
  result:=CalcValueColor(LegendIndex);
end;

Const TeeMsg_RadiusValues='Radius'; { <-- dont translate ! }

{ TCustomPolarSeries }

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

  Shadow.Visible:=False;
  FCloseCircle:=True;
  YValues.Name:=TeeMsg_RadiusValues;
  FAngleLabels:=TPolarLabels.Create(Self);
  FZoomSeries:=True;
end;

Destructor TCustomPolarSeries.Destroy;
Begin
  FreeAndNil(FAngleLabels);
  inherited;
end;

Procedure TCustomPolarSeries.SetParentChart(Const Value:TCustomAxisPanel);
Begin
  inherited;

  if Assigned(FAngleLabels) then
     FAngleLabels.SetParent(Value);
end;

Procedure TCustomPolarSeries.SetCloseCircle(Value:Boolean);
begin
  SetBooleanProperty(FCloseCircle,Value);
end;

Procedure TCustomPolarSeries.SetLabelsRotated(Value:Boolean);
begin
  FAngleLabels.Rotated:=Value;
end;

Procedure TCustomPolarSeries.DrawPolarCircle(HalfWidth,HalfHeight,Z:Integer);
var OldP : TPoint;
    P    : TPoint;
    tmp  : Double;
    t    : Integer;
begin
  With ParentChart.Canvas do
  begin
    if IMaxValuesCount=0 then
       EllipseWithZ( CircleXCenter-HalfWidth,CircleYCenter-HalfHeight,
                     CircleXCenter+HalfWidth,CircleYCenter+HalfHeight, Z)
    else
    begin
      tmp:=TeePiStep*AngleIncrement;
      AngleToPos(0,HalfWidth,HalfHeight,OldP.X,OldP.Y);
      MoveTo3D(OldP.X,OldP.Y,Z);

      for t:=0 to IMaxValuesCount do
      begin
        AngleToPos(t*tmp,HalfWidth,HalfHeight,P.X,P.Y);
        if Brush.Style<>bsClear then FillTriangle(OldP,P,Z);
        LineTo3D(P.X,P.Y,Z);
        OldP:=P;
      end;
    end;
  end;
end;

Function TCustomPolarSeries.GetCircleLabel(Const Angle:Double; Index:Integer):String;
var tmp : Double;
begin
  if FAngleLabels.ClockWise then
     tmp:=360-Angle
  else
     tmp:=Angle;

  if tmp=360 then tmp:=0;

  result:=FloatToStr(tmp)+TeePolarDegreeSymbol;
end;

type
  TAxisAccess=class(TChartAxis);
  TAxisPanelAccess=class(TCustomAxisPanel);

Procedure TCustomPolarSeries.DrawAxis;
var tmpValue     : Double;
    tmpIncrement : Double;
    tmpXRad      : Integer;
    tmpYRad      : Integer;

  Procedure SetPenCanvas(const APen:TTeePen);
  Begin
    with ParentChart.Canvas do
    begin
      Brush.Style:=bsClear;
      BackMode:=cbmTransparent;

      if APen.Color=clTeeColor then
         AssignVisiblePenColor(APen,{$IFDEF FMX}claGray{$ELSE}clGray{$ENDIF})
      else
         AssignVisiblePen(APen);
    end;
  end;

  Procedure DrawYGrid;

    Procedure InternalDrawGrid;
    begin
      DrawRing(tmpValue,EndZ);
      tmpValue:=tmpValue-tmpIncrement;
    end;

  Begin
    With GetVertAxis do
    if Grid.Visible then
    begin
      tmpIncrement:=CalcIncrement;

      if tmpIncrement>0 then
      begin
        SetPenCanvas(GetVertAxis.Grid);

        tmpValue:=Maximum/tmpIncrement;

        if (Abs(tmpValue)<MaxLongint) and
           (Abs((Maximum-Minimum)/tmpIncrement)<10000) then
        Begin
          if RoundFirstLabel then tmpValue:=tmpIncrement*Trunc(tmpValue)
                             else tmpValue:=Maximum;

          if LabelsOnAxis then
          begin
            While tmpValue>Maximum do tmpValue:=tmpValue-tmpIncrement;
            While tmpValue>=Minimum do InternalDrawGrid;
          end
          else
          begin
            While tmpValue>=Maximum do tmpValue:=tmpValue-tmpIncrement;
            While tmpValue>Minimum do InternalDrawGrid;
          end;
        end;
      end;
    end;
  end;

  Procedure DrawXGrid;

    procedure CurvePoints(const Center,Radius:TPointFloat; const Rotation:Single;
                          var Points:TPointFloatArray; const Bounds:{$IFDEF LCL}TeCanvas.{$ENDIF}TRectF);
    var tmpSin,
        tmpCos : Extended;

      procedure RotatePoint(out P:TPointFloat; const AX,AY:Single);
      begin
        P.X:=Center.x+(  AX*tmpCos + AY*tmpSin );
        P.Y:=Center.y+( -AX*tmpSin + AY*tmpCos );
      end;

    var t : Integer;
        Angle : Single;
        tmpX : Single;
        tmpP : TPointFloat;
    begin
      tmpX:=Center.X;

      for t:=0 to High(points) do
      begin
        tmpP.X:=(Points[t].x-tmpX);

        Angle:=ArcTan2((Center.Y-Bounds.Top),tmpP.X)-Pi*0.5;

        SinCos(Angle+Rotation,tmpSin,tmpCos);

        RotatePoint(points[t],0,points[t].y-Center.Y);
      end;
    end;

    procedure CurvedText(const Angle:Double; const S:String);
    var p : TTeeBasePath;

        points : TPointFloatArray;
        types : TPathPointTypes;
        bounds : {$IFDEF LCL}TeCanvas.{$ENDIF}TRectF;

        tmpCenter,
        tmpRadius : TPointFloat;

        tmpYPos : TCoordinate;
    begin
      tmpCenter.X:=CircleXCenter;
      tmpCenter.Y:=CircleYCenter;

      tmpRadius.x:=CircleWidth*0.5;
      tmpRadius.y:=CircleHeight*0.5;

      if (Angle>90) and (Angle<270) then
         tmpYPos:=CircleRect.Bottom+Round(0.2*ParentChart.Canvas.FontHeight)
      else
         tmpYPos:=CircleRect.Top-ParentChart.Canvas.FontHeight;

      p:=ParentChart.Canvas.CreatePath;
      p.TextOut(CircleXCenter-0.5*ParentChart.Canvas.TextWidth(S),tmpYPos,S);
      p.GetPoints(types,points,bounds);
      p.Free;

      CurvePoints(tmpCenter,tmpRadius,Angle*TeePiStep,Points,Bounds);

      p:=ParentChart.Canvas.CreatePath(Points,Types);

      ParentChart.Canvas.AssignBrush(AngleLabels.Font.Brush,AngleLabels.Font.Color);
      ParentChart.Canvas.AssignVisiblePen(AngleLabels.Font.OutLine);

      p.Draw(ParentChart.Canvas);
      p.Free;
    end;

    Procedure DrawAngleLabel(Angle:Double; AIndex:Integer); { MS : 5.02 - replaced old code }
    var X         : Integer;
        Y         : Integer;
        tmpHeight : Integer;
        AngleRad  : Double;
        tmpSt     : String;
        tmpMult   : Integer;
        tmpBorder : Integer;
        tmpMargin : Double;
        tmpXRadius: Integer;
        tmpYRadius: Integer;
        tmpAngle  : Integer;
        tmpR      : TRect;
        tmpW      : Integer;
        tmpM      : Integer;
    begin
      tmpHeight:=ParentChart.Canvas.FontHeight;

      if Angle>=360 then Angle:=Angle-360;

      tmpSt:=GetCircleLabel(Angle,AIndex);

      if Assigned(FOnGetCircleLabel) then
         FOnGetCircleLabel(Self,Angle,AIndex,tmpSt);

      if CircleLabelsInside then
      begin
        tmpXRadius:=tmpXRad;
        tmpYRadius:=tmpYRad;

        Dec(tmpXRadius,ParentChart.Canvas.TextWidth('   ')); // 4 chars: "360 "
        Dec(tmpYRadius,tmpHeight);
        tmpMargin:= -LabelsMargin*0.01;
      end
      else
      begin
        tmpBorder:=CirclePen.HalfWidth;

        tmpXRadius:=XRadius+tmpBorder;
        tmpYRadius:=YRadius+tmpBorder;

        tmpMargin:=LabelsMargin*0.01;
      end;

      Inc(tmpXRadius,Round(tmpXRadius*tmpMargin));
      Inc(tmpYRadius,Round(tmpYRadius*tmpMargin));

      AngleToPos(Angle*TeePiStep,tmpXRadius,tmpYRadius,X,Y);

      Angle:=Angle+RotationAngle;
      AngleRad:=Angle*TeePiStep;

      if CircleLabelsRotated then
      begin
        if (Angle>90) and (Angle<270) then
           tmpMult:= 1  { right aligned }
        else
           tmpMult:=-1; { left aligned }

        Inc(X, tmpMult*Round(0.5*tmpHeight*Sin(AngleRad)));
        Inc(Y, tmpMult*Round(0.5*tmpHeight*Cos(AngleRad)));
      end;

      if Angle>=360 then Angle:=Angle-360;

      if AngleLabels.Curved then
         CurvedText(Angle,tmpSt)
      else
      with ParentChart.Canvas do
      begin
        if FAngleLabels.Rotated then
        begin
          if (Angle>90) and (Angle<270) then
          begin
            TextAlign:=TA_RIGHT;
            Angle:=Angle+180;
          end
          else TextAlign:=TA_LEFT;

          RotateLabel3D(X,Y,EndZ,tmpSt,Angle,False);
        end
        else
        begin
          if (Angle=0) or (Angle=180) then
             Dec(Y,tmpHeight div 2)
          else
          if (Angle>0) and (Angle<180) then
             Dec(Y,tmpHeight);

          if (Angle=90) or (Angle=270) then
             TextAlign:=ta_Center
          else
          begin
            if FAngleLabels.Inside then
               if (Angle>90) and (Angle<270) then TextAlign:=ta_Left
                                             else TextAlign:=ta_Right
            else
               if (Angle>90) and (Angle<270) then TextAlign:=ta_Right
                                             else TextAlign:=ta_Left;
          end;

          {
          tmpWidth:=TextWidth('0') div 2;

          if Angle=0 then Inc(x,tmpWidth) else
          if Angle=180 then Dec(x,tmpWidth);
          }

          if not FAngleLabels.Transparent then
          begin
            if FAngleLabels.FRotated then
               tmpAngle:=Round(Angle)
            else
               tmpAngle:=0;

            tmpW:=TextWidth(tmpSt);

            tmpM:=Round(tmpHeight*0.1);

            if FAngleLabels.Pen.Visible then
               Inc(tmpM,{$IFDEF FMX}Round{$ENDIF}(FAngleLabels.Pen.Width) div 2);

            if TextAlign=TA_LEFT then
            begin
              tmpR.Left:=X-tmpM;
              tmpR.Right:=X+tmpW+tmpM;
            end
            else
            if TextAlign=TA_CENTER then
            begin
              tmpR.Left:=X-(tmpW div 2)-tmpM;
              tmpR.Right:=X+(tmpW div 2)+tmpM;
            end
            else
            begin
              tmpR.Right:=X+tmpM;
              tmpR.Left:=X-tmpW-tmpM;
            end;

            tmpM:=Round(tmpHeight*0.04);

            if FAngleLabels.Pen.Visible then
               Inc(tmpM,{$IFDEF FMX}Round{$ENDIF}(FAngleLabels.Pen.Width) div 2);

            tmpR.Top:=Y-tmpM;
            tmpR.Bottom:=Y+tmpHeight+tmpM;

            FAngleLabels.Draw(tmpR,tmpAngle,EndZ);
          end;

          TextOut3D(X,Y,EndZ,tmpSt);
        end;
      end;
    end;

  var
    tmpNewX  : Integer;
    tmpNewY  : Integer;

    procedure DrawTicks(const APen:TTeePen; ACount,ALength:Integer; const AStep:Double);
    var t        : Integer;
        tmpAngle : Double;
        tmpX0    : Integer;
        tmpY0    : Integer;
        tmpX1    : Integer;
        tmpY1    : Integer;
        tmpXRad0 : Double;
        tmpYRad0 : Double;
        tmp      : Double;
    begin
      SetPenCanvas(APen);

      tmp:=1-(ALength*0.01);
      tmpXRad0:=tmpNewX*tmp;
      tmpYRad0:=tmpNewY*tmp;

      tmpValue:=0;

      while tmpValue<(360-MinAxisIncrement) do
      begin
        for t:=1 to ACount do
        begin
          tmpAngle:=TeePiStep*(tmpValue+AStep*t);
          AngleToPos(tmpAngle,tmpXRad0,tmpYRad0,tmpX0,tmpY0);
          AngleToPos(tmpAngle,tmpNewX,tmpNewY,tmpX1,tmpY1);

          ParentChart.Canvas.LineWithZ(tmpX0,tmpY0,tmpX1,tmpY1,EndZ);
        end;

        tmpValue:=tmpValue+tmpIncrement;
      end;
    end;

  var tmpX     : Integer;
      tmpY     : Integer;
      tmpH     : Integer;
      tmpIndex : Integer;
      tmpMax   : Integer;
      tmpNum   : Integer;
      tmpDrawEvery : Integer;
  begin
    with GetHorizAxis do
    if Visible or Self.CircleLabels then
    begin
      tmpIncrement:=Increment;
      if tmpIncrement<=0 then tmpIncrement:=10.0;

      if Pointer.Visible and Pointer.InflateMargins then
      begin
        tmpNewX:=tmpXRad+Pointer.HorizSize;
        tmpNewY:=tmpYRad+Pointer.VertSize;
      end
      else
      begin
        tmpNewX:=tmpXRad;
        tmpNewY:=tmpYRad;
      end;

      if Grid.Visible then
      begin
        tmpValue:=0;

        SetPenCanvas(Grid);

        while tmpValue<(360-MinAxisIncrement) do
        begin
          AngleToPos(TeePiStep*tmpValue,tmpNewX,tmpNewY,tmpX,tmpY);
          ParentChart.Canvas.LineWithZ(CircleXCenter,CircleYCenter,tmpX,tmpY,EndZ);

          tmpValue:=tmpValue+tmpIncrement;
        end;
      end;

      // Minor Ticks only for Polar. Radar should not draw minor ticks outside the "circle"
      if IMaxValuesCount=0 then
         if MinorTicks.Visible and (MinorTickCount>0) then
            DrawTicks(MinorTicks,MinorTickCount,MinorTickLength,tmpIncrement/(1+MinorTickCount));

      if Ticks.Visible then
         DrawTicks(Ticks,1,TickLength,tmpIncrement);

      if IShouldDrawLabels { AngleLabels.Visible } then
      begin
        tmpDrawEvery:=1;

        ParentChart.PrepareFont3D(Self.AngleLabels.Font);

        if AngleLabels.AntiOverlap then
        begin
          tmpH:=Max(2,ParentChart.Canvas.FontHeight);
          tmpMax:=Max(1,Round(CircleHeight/tmpH));
          tmpNum:=Round(180/tmpIncrement);

          if tmpNum>tmpMax then
             tmpDrawEvery:=Max(1,tmpNum div tmpMax);
        end;

        tmpIndex:=0;
        tmpValue:=0;

        while tmpValue<(360-MinAxisIncrement) do
        begin
          if (tmpIndex mod tmpDrawEvery)=0 then
             DrawAngleLabel(tmpValue,tmpIndex);

          tmpValue:=tmpValue+tmpIncrement;
          Inc(tmpIndex);
        end;
      end;
    end;
  end;

var tmp : Integer;
begin
  TAxisPanelAccess(ParentChart).DoOnBeforeDrawAxes;

  tmpXRad:=AxisXRadius;
  tmpYRad:=AxisYRadius;

  // Always call DrawXGrid to allow drawing the AngleLabels, even when Axes.Visible=False
  DrawXGrid;

  with ParentChart do
  if Axes.Visible then
  begin
    DrawYGrid;

    with RightAxis do
    if Visible then
    begin
      if IMaxValuesCount>0 then
         TAxisAccess(RightAxis).InternalSetInverted(GetVertAxis.Inverted);

      Logarithmic:=GetVertAxis.Logarithmic;

      tmp:=CircleXCenter+TAxisAccess(RightAxis).SizeTickAxis;

      CustomDrawMinMaxStartEnd( tmp,tmp+TAxisAccess(RightAxis).SizeLabels,CircleXCenter,False,
                                GetVertAxis.Minimum,
                                GetVertAxis.Maximum,
                                GetVertAxis.Increment,
                                CircleYCenter-tmpYRad,CircleYCenter);
    end;

    if IMaxValuesCount=0 then
    begin
      with LeftAxis do
      if Visible then
      begin
        TAxisAccess(LeftAxis).InternalSetInverted(True);
        Logarithmic:=GetHorizAxis.Logarithmic;

        tmp:=CircleXCenter-TAxisAccess(LeftAxis).SizeTickAxis;

        CustomDrawMinMaxStartEnd( tmp,tmp-TAxisAccess(LeftAxis).SizeLabels,CircleXCenter,False,
                                  GetVertAxis.Minimum,
                                  GetVertAxis.Maximum,
                                  GetVertAxis.Increment,
                                  CircleYCenter,CircleYCenter+tmpYRad);

        TAxisAccess(LeftAxis).InternalSetInverted(False);
      end;

      with TopAxis do
      if Visible then
      begin
        Logarithmic:=GetHorizAxis.Logarithmic;

        TAxisAccess(TopAxis).InternalSetInverted(True);
        tmp:=CircleYCenter-TAxisAccess(TopAxis).SizeTickAxis;

        CustomDrawMinMaxStartEnd( tmp,tmp-TAxisAccess(TopAxis).SizeLabels,CircleYCenter,False,
                                  GetHorizAxis.Minimum,
                                  GetHorizAxis.Maximum,
                                  GetHorizAxis.Increment,
                                  CircleXCenter-tmpXRad,CircleXCenter);

        TAxisAccess(TopAxis).InternalSetInverted(False);
      end;

      With BottomAxis do
      if Visible then
      begin
        Logarithmic:=GetHorizAxis.Logarithmic;

        tmp:=CircleYCenter+TAxisAccess(BottomAxis).SizeTickAxis;

        CustomDrawMinMaxStartEnd( tmp,tmp+TAxisAccess(BottomAxis).SizeLabels,CircleYCenter,False,
                                  GetHorizAxis.Minimum,
                                  GetHorizAxis.Maximum,
                                  GetHorizAxis.Increment,
                                  CircleXCenter,CircleXCenter+tmpXRad);
      end;
    end;
  end;
end;

type
  TPanelAccess=class(TCustomAxisPanel);

Procedure TCustomPolarSeries.DoBeforeDrawValues;

  Procedure DrawCircle;
  var R : TRect;
      tmpR : TRect;
  begin
    DrawCircleShadow;

    With ParentChart,Canvas do
    Begin
      AssignBrush(CircleBrush, CalcCircleBackColor);

      if HasBackImage and BackImage.Inside
         and ((not View3D) or (View3DOptions.Orthogonal)) then
      begin
        R:=CircleRect;

        {$IFDEF FMX}
        R.Offset(1,1);
        {$ELSE}
        Inc(R.Left);
        Inc(R.Top);
        Inc(R.Right);
        Inc(R.Bottom);
        {$ENDIF}

        if View3D then tmpR:=CalcRect3D(R,Width3D)
                  else tmpR:=R;

        ClipEllipse(tmpR);

        Brush.Image:=BackImage;
        TPanelAccess(ParentChart).DrawBitmap(R,Width3D);
        Brush.Clear;

        UnClipRectangle;
      end;

      AssignVisiblePen(CirclePen);

      DrawPolarCircle(CircleWidth div 2,CircleHeight div 2,EndZ);
    end;
  end;

  function ShouldDrawLabels:Boolean;
  var tmpW : Integer;
      tmp : Integer;
  begin
    result:=FAngleLabels.Visible;

    if result and (not FAngleLabels.Inside) then
    begin
      ParentChart.PrepareFont3D(FAngleLabels.Font);

      tmpW:=ParentChart.Canvas.TextWidth('360');

      with ParentChart.ChartRect do
      begin
        if (Right-Left)<3*tmpW then
           result:=False
        else
        begin
          if FAngleLabels.Rotated then
          begin
            Top:=Top+tmpW;
             Bottom:=Bottom-tmpW;
          end
          else
          begin
            tmp:=ParentChart.Canvas.FontHeight+2;
            Top:=Top+tmp;
            Bottom:=Bottom-tmp;
          end;

          Left:=Left+tmpW;
          Right:=Right-tmpW;
        end;
      end;
    end;
  end;

var t   : Integer;
    tmp : Integer;
    First : Boolean;
begin
  tmp:=-CirclePen.HalfWidth;
  if tmp<>0 then
     InflateRect(ParentChart.ChartRect,tmp,tmp);

  inherited;

  First:=False;

  IShouldDrawLabels:=True;

  with ParentChart do
  for t:=0 to SeriesCount-1 do
  if (Series[t].Active) and (Series[t] is Self.ClassType) then
  begin
    if Series[t]=Self then
    begin
      if not First then
      begin
        IShouldDrawLabels:=ShouldDrawLabels;

        if not IShouldDrawLabels then
           break;
      end;

      break;
    end;

    First:=True;
  end;

  inherited;

  First:=False;
  With ParentChart do
  for t:=0 to SeriesCount-1 do
  if (Series[t].Active) and (Series[t] is Self.ClassType) then
  begin
    if Series[t]=Self then
    begin
      if not First then
      begin
        DrawFrame;

        DrawCircle;

        if DrawAxesBehind then
           DrawAxis;
      end;

      break;
    end;

    First:=True;
  end;
end;

Procedure TCustomPolarSeries.CalcXYPos( ValueIndex:Integer;
                                        ARadius:Integer;
                                        out X,Y:Integer);
begin
  CalcXYPosition(XValue[ValueIndex],YValues.Value[ValueIndex],ARadius,X,Y);
end;

procedure TCustomPolarSeries.CalcXYPosition(const XValue, YValue: TChartValue;
  ARadius: Integer; Axis: TChartAxis; out X, Y: Integer);

  Function CalcLogRadius(const tmpDif:Double):Double;
  var IAxisLogSizeRange : Double;
      ILogMin           : Double;
      ILogMax           : Double;
  begin
    ILogMax:=ln(Axis.Maximum);

    if Axis.Minimum<=0 then
       ILogMin:=0
    else
       ILogMin:=ln(Axis.Minimum);

    IAxisLogSizeRange:=ARadius/(ILogMax-ILogMin);

    if Axis.Inverted then
       result:=Round((ILogMax-ln(tmpDif))*IAxisLogSizeRange)
    else
       result:=Round((ln(tmpDif)-ILogMin)*IAxisLogSizeRange);
  end;

var tmp       : Double;
    tmpDif    : Double;
    tmpAtEnd  : Boolean;
    tmpRadius : Double;
begin
  With Axis do tmp:=Maximum-Minimum;

  if Axis.Inverted then
     tmpDif:=Axis.Maximum-YValue
  else
     tmpDif:=YValue-Axis.Minimum;

  if Axis.Logarithmic then tmpAtEnd:=tmpDif<=0
                      else tmpAtEnd:=tmpDif<0;

  if (tmp=0) or tmpAtEnd then
  begin
    X:=CircleXCenter;
    Y:=CircleYCenter;
  end
  else
  begin
    if Axis.Logarithmic then
       tmpRadius:=CalcLogRadius(tmpDif)
    else
       tmpRadius:=tmpDif*ARadius/tmp;

    AngleToPos(TeePiStep*XValue,tmpRadius,tmpRadius,X,Y);
  end;
end;

Function TCustomPolarSeries.CalcYPos(ValueIndex:Integer):Integer;
var tmpX : Integer;
begin
  CalcXYPos(ValueIndex,AxisYRadius,tmpX,result);
end;

Function TCustomPolarSeries.CalcXPos(ValueIndex:Integer):Integer;
var tmpY : Integer;
begin
  CalcXYPos(ValueIndex,AxisXRadius,result,tmpY);
end;

procedure TCustomPolarSeries.LinePrepareCanvas(ValueIndex:Integer);
var tmpColor : TColor;
begin
  if Pen.Visible then
  begin
    tmpColor:=Pen.Color;

    if (ValueIndex=TeeAllValues) or (tmpColor=clTeeColor) then
       tmpColor:=SeriesColor
    else
    if ColorEachPoint and ColorEachLine then
       tmpColor:=ValueColor[ValueIndex];

    ParentChart.Canvas.AssignVisiblePenColor(Pen,tmpColor);
    ParentChart.Canvas.BackMode:=cbmTransparent;
  end
  else
    ParentChart.Canvas.Pen.Style:=psClear;
end;

type
  TCanvasAccess=class(TTeeCanvas);

Procedure TCustomPolarSeries.FillTriangle(Const A,B:TPoint; Z:Integer);
var tmpStyle : TPenStyle;
begin
  TCanvasAccess(ParentChart.Canvas).SetAntiAlias(False);

  with ParentChart.Canvas do
  begin
    tmpStyle:=Pen.Style;
    Pen.Style:=psClear;
    TriangleWithZ(A,B,TeePoint(CircleXCenter,CircleYCenter),Z);
    Pen.Style:=tmpStyle;
  end;

  TCanvasAccess(ParentChart.Canvas).SetAntiAlias(True);
end;

procedure TCustomPolarSeries.InternalDrawValue(ValueIndex,X,Y:Integer);

  Procedure TryFillTriangle;
  var tmpBlend : TTeeBlend;
      R        : TRect;
  begin
    if Brush.Style<>bsClear then
    begin
      ParentChart.SetBrushCanvas(CalcValueColor(ValueIndex),Brush,Brush.Color);

      // Only for old GDI.  For GDI+, see DrawAllValues.
      if (Transparency>0) and (not ParentChart.Canvas.SupportsAlpha) then
      begin
        R.Left  :=Math.Min(CircleXCenter,Math.Min(OldX,X));
        R.Top   :=Math.Min(CircleYCenter,Math.Min(OldY,Y));
        R.Right :=Math.Max(CircleXCenter,Math.Max(OldX,X));
        R.Bottom:=Math.Max(CircleYCenter,Math.Max(OldY,Y));

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

        tmpBlend:=ParentChart.Canvas.BeginBlending(R,Transparency);

        FillTriangle(TeePoint(OldX,OldY),TeePoint(X,Y),StartZ);
        ParentChart.Canvas.EndBlending(tmpBlend);
      end
      else
        FillTriangle(TeePoint(OldX,OldY),TeePoint(X,Y),StartZ);

      LinePrepareCanvas(ValueIndex);
    end;
  end;

begin
  With ParentChart.Canvas do
  if ValueIndex=FirstDisplayedIndex then
     MoveTo3D(X,Y,StartZ)  { <-- first point }
  else
  begin
    if (X<>OldX) or (Y<>OldY) then
    begin
      TryFillTriangle;
      LineTo3D(X,Y,StartZ);
    end;

    if ((ValueIndex=LastDisplayedIndex) and CloseCircle) then
    begin
      if ColorEachPoint and (Brush.Style<>bsClear) then
        AssignVisiblePenColor(Pen,ValueColor[FirstDisplayedIndex]);

      OldX:=X;
      OldY:=Y;
      X:=CalcXPos(FirstDisplayedIndex);
      Y:=CalcYPos(FirstDisplayedIndex);
      TryFillTriangle;

      LineTo3D(X,Y,StartZ);

      X:=OldX;
      Y:=OldY;
    end;
  end;
end;

procedure TCustomPolarSeries.DrawValue(ValueIndex:Integer);
var X : Integer;
    Y : Integer;
    tmp : Integer;

  procedure DoDraw;
  begin
    X:=CalcXPos(ValueIndex);
    Y:=CalcYPos(ValueIndex);

    LinePrepareCanvas(ValueIndex);
    InternalDrawValue(ValueIndex,X,Y);

    OldX:=X;
    OldY:=Y;
  end;

begin
  if IsPainted(ValueIndex) then
     DoDraw
  else
  if TreatNulls=tnDontPaint then
  begin
    tmp:=ValueIndex+1;
    if (tmp=Count) then tmp:=0;

    OldX:=CalcXPos(tmp);
    OldY:=CalcYPos(tmp);

    ParentChart.Canvas.MoveTo3D(OldX, OldY, startZ);
  end;
end;

type
  TPointerAccess=class(TSeriesPointer);

Procedure TCustomPolarSeries.CalcHorizMargins(var LeftMargin,RightMargin:Integer);
begin
  inherited;
  TPointerAccess(Pointer).CalcHorizMargins(LeftMargin,RightMargin);
end;

Procedure TCustomPolarSeries.CalcVerticalMargins(var TopMargin,BottomMargin:Integer);
begin
  inherited;
  TPointerAccess(Pointer).CalcVerticalMargins(TopMargin,BottomMargin);
end;

Procedure TCustomPolarSeries.SetSeriesColor(AColor:TColor);
begin
  inherited;

  if Brush.Style=bsClear then
     Pen.Color:=AColor;
end;

// Deprecated:

function TCustomPolarSeries.GetCircleLabels:Boolean;
begin
  result:=FAngleLabels.Visible;
end;

function TCustomPolarSeries.GetCircleLabelsFont:TTeeFont;
begin
  result:=FAngleLabels.Font;
end;

function TCustomPolarSeries.GetCircleLabelsInside:Boolean;
begin
  result:=FAngleLabels.Inside;
end;

function TCustomPolarSeries.GetCircleLabelsRot:Boolean;
begin
  result:=FAngleLabels.Rotated;
end;

function TCustomPolarSeries.GetClockWiseLabels:Boolean;
begin
  result:=FAngleLabels.ClockWise;
end;

function TCustomPolarSeries.GetLabelsMargin:Integer;
begin
  result:=FAngleLabels.Margin;
end;

Procedure TCustomPolarSeries.SetCircleLabels(Value:Boolean);
Begin
  FAngleLabels.Visible:=Value;
end;

Procedure TCustomPolarSeries.SetCircleLabelsFont(const Value:TTeeFont);
begin
  FAngleLabels.Font:=Value;
end;

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

{ The BottomAxis is used in Angle axis }
Function TCustomPolarSeries.GetAngleIncrement:Double;
Const MinAngle = 10;
begin
  if ParentChart=nil then
     result:=MinAngle
  else
  begin
    result:=GetHorizAxis.Increment;

    if result=0 then
       result:=MinAngle;
  end;
end;

procedure TCustomPolarSeries.SetAngleLabels(const Value:TPolarLabels);
begin
  FAngleLabels.Assign(Value);
end;

{ The BottomAxis is used in Angle axis }
Procedure TCustomPolarSeries.SetAngleIncrement(Const Value:Double);
begin
  if Assigned(ParentChart) then
     GetHorizAxis.Increment:=Value;
end;

{ The LeftAxis is used in Radius axis }
Function TCustomPolarSeries.GetRadiusIncrement:Double;
begin
  if ParentChart=nil then result:=0
                     else result:=GetVertAxis.Increment;
end;

{ The LeftAxis is used in Radius axis }
Procedure TCustomPolarSeries.SetRadiusIncrement(Const Value:Double);
begin
  if Assigned(ParentChart) then
     GetVertAxis.Increment:=Value;
end;

Function TCustomPolarSeries.Clicked(x,y:Integer):Integer;
var Old  : TPoint;
    P    : TPoint;
    tmp  : TPoint;
    tmpCenter : TPoint;

  function ClickedSegment:Boolean;
  begin
    result:=((Brush.Style<>bsClear) and PointInTriangle(P,tmpCenter,tmp,Old)) or
            (Pen.Visible and PointInLine(P,tmp,Old));
  end;

var t : Integer;
begin
  result:=inherited Clicked(x,y);

  if (result=TeeNoPointClicked) and
     (FirstValueIndex>-1) and (LastValueIndex>-1) then
  begin
    if Assigned(ParentChart) then
       ParentChart.Canvas.Calculate2DPosition(X,Y,StartZ);

    Old.X:=0;
    Old.Y:=0;
    P.X:=X;
    P.Y:=Y;

    tmpCenter.X:=CircleXCenter;
    tmpCenter.Y:=CircleYCenter;

    for t:=FirstValueIndex to LastValueIndex do
    begin
      tmp.X:=CalcXPos(t);
      tmp.Y:=CalcYPos(t);

      if (tmp.X=X) and (tmp.Y=Y) then { clicked right on point }
      begin
        result:=t;
        break;
      end
      else
      if Pointer.Visible and
         (Abs(tmp.X-X)<Pointer.HorizSize) and
         (Abs(tmp.Y-Y)<Pointer.VertSize) then
      begin
        result:=t;
        break;
      end
      else
      if (t>FirstValueIndex) and ClickedSegment then
      begin
        result:=t-1;
        break;
      end;

      Old:=tmp;
    end;

    // Finally, check if point P is over the last segment (only if CloseCircle = True)
    if (result=TeeNoPointClicked) and CloseCircle and (LastValueIndex>FirstValueIndex) then
    begin
      Old.X:=CalcXPos(FirstValueIndex);
      Old.Y:=CalcYPos(FirstValueIndex);

      if ClickedSegment then
         result:=LastValueIndex;
    end;
  end;
end;

Procedure TCustomPolarSeries.DrawLegendShape(ValueIndex:Integer; Const Rect:TRect);

  procedure DoDrawPointer(Behind:Boolean);
  var Old      : TColor;
      tmpColor : TColor;
      tmp      : TSeriesPointerStyle;
  begin
    if FPointer.Visible and (Behind=PointerBehind) then
    begin
      tmp:=Pointer.Style;
      Old:=Pointer.Color;

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

      if tmpColor<>clNone then
         if Pointer.Color<>clTeeColor then
            tmpColor:=Pointer.Color;

      if ValueIndex<>TeeAllValues then
        if Assigned(FOnGetPointerStyle) then
        begin
          Pointer.Color:=tmpColor;
          tmp:=FOnGetPointerStyle(Self,ValueIndex);
          tmpColor:=Pointer.Color;
        end;

      TeePointerDrawLegend(FPointer,tmpColor,Rect,Pen.Visible,tmp);

      Pointer.Color:=Old;
    end
  end;

var tmpY : TCoordinate;
begin
  DoDrawPointer(True);

  if Pen.Visible then
  begin
    LinePrepareCanvas(ValueIndex);

    with Rect do
    begin
      with ParentChart.Canvas.Pen do
           Width:=Min(Width,Abs(Bottom-Top));

      tmpY:=(Top+Bottom) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF};
      ParentChart.Canvas.DoHorizLine(Left,Right,tmpY);
    end;
  end;

  if FPointer.Visible then
     DoDrawPointer(False)
  else
  if not Pen.Visible then
     inherited
end;

{ Used to draw inside filled circles (grid) (or radar grid lines) }
{ Can be used also to custom draw circle zones at specific values }
Procedure TCustomPolarSeries.DrawZone(Const Min,Max:Double; Z:Integer);
var tmpX : Integer;
    tmpY : Integer;
    tmpP : TPoint;
begin
  tmpP:=TeePoint(CircleXCenter,CircleYCenter);

  CalcXYRadius(Min,tmpX,tmpY);
  ParentChart.Canvas.ClipEllipse(RectFromCenter(tmpP,tmpX,tmpY),True);

  CalcXYRadius(Max,tmpX,tmpY);
  ParentChart.Canvas.Ellipse(RectFromCenter(tmpP,tmpX,tmpY));

  ParentChart.Canvas.UnClipRectangle;
end;

Procedure TCustomPolarSeries.CalcXYRadius(Const Value:Double; out X,Y:Integer);
Var tmp : Double;
begin
  with GetVertAxis do
  begin
    tmp:=Maximum-Minimum;

    if tmp<>0 then
    begin
      tmp:=(Value-Minimum)/tmp;
      X:=Round(tmp*AxisXRadius);
      Y:=Round(tmp*AxisYRadius);
    end
    else
    begin
      X:=0;
      Y:=0;
    end;
  end;
end;

{ Used to draw inside circles (grid) (or radar grid lines) }
{ Can be used also to custom draw circles at specific values }
Procedure TCustomPolarSeries.DrawRing(Const Value:Double; Z:Integer);
var tmpX : Integer;
    tmpY : Integer;
begin
  ParentChart.Canvas.Brush.Style:=bsClear;
  CalcXYRadius(Value,tmpX,tmpY);
  DrawPolarCircle(tmpX,tmpY,Z);
end;

Procedure TCustomPolarSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TCustomPolarSeries then
  with TCustomPolarSeries(Source) do
  begin
    Self.FAngleLabels.Assign(FAngleLabels);
  end;

  inherited;
end;

Procedure TCustomPolarSeries.Assign(Source:TPersistent);
begin
  if Source is TCustomPolarSeries then
  with TCustomPolarSeries(Source) do
  begin
    Self.FCloseCircle:= FCloseCircle;
  end;

  inherited;
end;

Function TCustomPolarSeries.GetRadiusValues:TChartValueList;
begin
  result:=YValues;
end;

Procedure TCustomPolarSeries.SetRadiusValues(const Value:TChartValueList);
begin
  SetYValues(Value); { overrides the default YValues }
end;

Function TCustomPolarSeries.IsRadiusStored:Boolean;
begin
  With YValues do
  result:=(Name<>TeeMsg_RadiusValues) or DateTime or
          {$IFDEF TEEMULTIPLIER}(Multiplier<>1) or {$ENDIF}
          (Order<>loNone) or (ValueSource<>'');
end;

Function TCustomPolarSeries.GetAngleValues:TChartValueList;
begin
  result:=XValues;
end;

Procedure TCustomPolarSeries.SetAngleValues(Value:TChartValueList);
begin
  SetXValues(Value); { overrides the default XValues }
end;

Procedure TCustomPolarSeries.PrepareForGallery;
Begin
  inherited;

  Circled:=True;
  GetHorizAxis.Increment:=90;

  With ParentChart do
  begin
    Chart3DPercent:=5;
    RightAxis.Labels:=False;
    TopAxis.Labels:=False;

    With View3DOptions do
    begin
      Orthogonal:=False;
      Elevation:=360;
      Zoom:=90;
    end;
  end;
end;

procedure TCustomPolarSeries.SetClockWiseLabels(Value: Boolean);
begin
  FAngleLabels.ClockWise:=Value;
end;

class procedure TCustomPolarSeries.CreateSubGallery(
  AddSubChart: TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_NoPoint);
  AddSubChart(TeeMsg_NoLine);
  AddSubChart(TeeMsg_Filled);
  AddSubChart(TeeMsg_Labels);
  AddSubChart(TeeMsg_NoBorder);
  AddSubChart(TeeMsg_NoCircle);
end;

class procedure TCustomPolarSeries.SetSubGallery(ASeries: TChartSeries;
  Index: Integer);
begin
  With TCustomPolarSeries(ASeries) do
  Case Index of
    1: Pointer.Hide;
    2: Pen.Hide;
    3: Brush.Style:=bsSolid;
    4: CircleLabels:=True;
    5: Pointer.Pen.Hide;
    6: CirclePen.Hide;
  else inherited;
  end;
end;

procedure TCustomPolarSeries.SetCircleLabelsInside(Value: Boolean);
begin
  FAngleLabels.Inside:=Value;
end;

type
  TChartAccess=class(TCustomAxisPanel);

Function TCustomPolarSeries.DrawAxesBehind:Boolean;
begin
  result:=ParentChart.Axes.Behind;

  if ParentChart.View3D and (not ParentChart.View3DOptions.Orthogonal) and
     TChartAccess(ParentChart).DrawBackWallAfter(ParentChart.Width3D) then
        result:=not result;
end;

procedure TCustomPolarSeries.DoAfterDrawValues;
var tmp : Boolean;
    t   : Integer;
begin
  if not DrawAxesBehind then
  begin
    tmp:=False;

    With ParentChart do
    for t:=Self.SeriesIndex+1 to SeriesCount-1 do
        if Series[t] is Self.ClassType then
        begin
          tmp:=True;
          break;
        end;

    if not tmp then
       DrawAxis;
  end;

  inherited;
end;

Procedure TCustomPolarSeries.DrawMark( ValueIndex:Integer; Const St:String;
                                       APosition:TSeriesMarkPosition);
begin
  Marks.ApplyArrowLength(APosition);
  inherited;
end;

Procedure TCustomPolarSeries.AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False);
var t   : Integer;
    tmp : Double;
Begin
  tmp:=360.0/NumValues;
  for t:=1 to NumValues do
      AddXY( t*tmp,                      { <-- Angle }
             1+RandomValue(ChartSamplesMax)  { <-- Value (Radius) }
             );
end;

Procedure TCustomPolarSeries.CalcXYPosition(const XValue,YValue:TChartValue;
                                            ARadius:Integer; out X,Y:Integer);
begin
  CalcXYPosition(XValue, YValue, ARadius, GetVertAxis, X, Y);
end;

function TCustomPolarSeries.DrawValuesForward: Boolean;
begin
  result:=True;
end;

procedure TCustomPolarSeries.ScreenToPoint(X,Y,ValueIndex:Integer; ChangeX,ChangeY:Boolean);
begin
  if ChangeX then
     XValues[ValueIndex]:=PointToAngle(x,y)*HalfDivPi;

  if ChangeY then
     YValues[ValueIndex]:=PointToRadius(x,y);

  Repaint;
end;

procedure TCustomPolarSeries.SetLabelsMargin(Value: Integer);
begin
  FAngleLabels.Margin:=Value;
end;

function TCustomPolarSeries.GetAngleValue(ValueIndex: Integer): TChartValue;
begin
  result:=XValues.Value[ValueIndex]
end;

procedure TCustomPolarSeries.SetAngleValue(Index: Integer; const Value: TChartValue);
begin
  XValues[Index]:=Value;
  Repaint;
end;

function TCustomPolarSeries.ShouldCloseCircle:Boolean;
begin
  result:=CloseCircle;
end;

{ TPolarSeries }

Constructor TPolarSeries.Create(AOwner: TComponent);
begin
  inherited;
  FAngleValues:=TChartValueList.Create(Self, 'AngleValues');
  FAngleValues.Order:=loAscending;
  XValues.Order:=loNone;
end;

Function TPolarSeries.AddPolar( Const Angle,Value:Double;
                                Const ALabel:String;
                                AColor:TColor):Integer;
begin
  result:=AddXY(Value,Value,ALabel,AColor);

  if FAngleLabels.ClockWise then
    FAngleValues.Items[result]:=360-Angle
  else
    FAngleValues.Items[result]:=Angle;
end;

Function TPolarSeries.AddNull(Const ALabel:String=''):Integer;
begin
  result:=AddPolar(0,0,ALabel);
  SetNull(result);
end;

Function TPolarSeries.AddNullXY(Const X,Y:TChartValue; Const ALabel:String=''):Integer;
begin
  result:=AddPolar(X,Y,ALabel);
  SetNull(result);
end;

procedure TPolarSeries.AddSampleValues(NumValues: Integer;
  OnlyMandatory: Boolean);
var t   : Integer;
    tmp : Double;
Begin
  tmp:=360.0/NumValues;
  for t:=1 to NumValues do
      AddPolar( t*tmp,                      { <-- Angle }
             1+RandomValue(ChartSamplesMax)  { <-- Value (Radius) }
             );
end;

function TPolarSeries.CalcXPos(ValueIndex: Integer): Integer;
var tmpY : Integer;
begin
  CalcXYPosition(FAngleValues[ValueIndex],YValues[ValueIndex],AxisXRadius,GetHorizAxis,result,tmpY);
end;

function TPolarSeries.CalcYPos(ValueIndex: Integer): Integer;
var tmpX : Integer;
begin
  CalcXYPosition(FAngleValues[ValueIndex],YValues.Value[ValueIndex],AxisYRadius,GetVertAxis,tmpX,result);
end;

function TPolarSeries.CalcYPosValue(const Value: Double): Integer;
begin
  result:=inherited CalcYPosValue((GetVertAxis.Maximum + GetVertAxis.Minimum) - Value);
end;

function TPolarSeries.CanZoomScroll: Boolean;
begin
  result:=True;
end;

procedure TPolarSeries.DoAfterDrawValues;
begin
  if ParentChart.ClipPoints then ParentChart.Canvas.UnClipRectangle;
  inherited;
end;

procedure TPolarSeries.DoBeforeDrawValues;
var R, tmpR: TRect;
begin
  inherited;

  if ParentChart.ClipPoints then
  begin
    with ParentChart,Canvas do
    begin
      R:=CircleRect;

      {$IFDEF FMX}
      R.Offset(1,1);
      {$ELSE}
      Inc(R.Left);
      Inc(R.Top);
      Inc(R.Right);
      Inc(R.Bottom);
      {$ENDIF}

      if View3D then tmpR:=CalcRect3D(R,Width3D)
                else tmpR:=R;

      ClipEllipse(tmpR);
    end;
  end;
end;

function TPolarSeries.GetAngleValues: TChartValueList;
begin
  result:=FAngleValues;
end;

procedure TPolarSeries.SetAngleValues(Value: TChartValueList);
begin
  FAngleValues:=Value;
end;

function TPolarSeries.GetAngleValue(ValueIndex: Integer): TChartValue;
begin
  result:=FAngleValues.Value[ValueIndex];
end;

procedure TPolarSeries.SetAngleValue(Index: Integer; const Value: TChartValue);
begin
  FAngleValues[Index]:=Value;
  Repaint;
end;

procedure TPolarSeries.ScreenToPoint(X,Y,ValueIndex:Integer; ChangeX,ChangeY:Boolean);
begin
  if ChangeX then
     AngleValue[ValueIndex]:=PointToAngle(x,y)*HalfDivPi;

  if ChangeY then
  begin
    XValues[ValueIndex]:=PointToRadius(x,y);
    YValues[ValueIndex]:=XValues[ValueIndex];
  end;

  Repaint;
end;

function TPolarSeries.UseAxis: Boolean;
begin
  result:=False;
end;

function TPolarSeries.XScreenToValue(ScreenPos: Integer): Double;
begin
  result:=inherited XScreenToValue(ScreenPos);
  result:=Abs(result);
end;

function TPolarSeries.YScreenToValue(ScreenPos: Integer): Double;
begin
  result:=inherited YScreenToValue(ScreenPos);
  result:=Abs((GetVertAxis.Maximum + GetVertAxis.Minimum) - result);
end;

{ TRadarSeries }

Procedure TRadarSeries.DoBeforeDrawChart;
begin
  inherited;
  SetRotationAngle(90);
  IMaxValuesCount:=ParentChart.GetMaxValuesCount;

  if IMaxValuesCount>0 then
     AngleIncrement:=360.0/IMaxValuesCount;
end;

Procedure TRadarSeries.AddSampleValues(NumValues:Integer; OnlyMandatory:Boolean=False);
var RadarSampleStr : Array[0..4] of String;
    t : Integer;
begin
  RadarSampleStr[0]:=TeeMsg_PieSample1;
  RadarSampleStr[1]:=TeeMsg_PieSample2;
  RadarSampleStr[2]:=TeeMsg_PieSample3;
  RadarSampleStr[3]:=TeeMsg_PieSample4;
  RadarSampleStr[4]:=TeeMsg_PieSample5;

  for t:=0 to NumValues-1 do
      Add(RandomValue(1000),RadarSampleStr[t mod 5]);
end;

Procedure TRadarSeries.PrepareForGallery;
Begin
  inherited;
  FillSampleValues(NumSampleValues);
end;

Function TRadarSeries.GetCircleLabel(Const Angle:Double; Index:Integer):String;
begin
  if ClockWiseLabels then
     Index:=Count-1-Index;

  result:=Labels[Index];

  if result='' then
     result:=TeeStr(Index);
end;

Function TRadarSeries.NumSampleValues:Integer;
begin
  result:=5;
end;

Function TRadarSeries.GetxValue(ValueIndex:Integer):TChartValue;
begin
  if ClockWiseLabels then ValueIndex:=Count-1-ValueIndex;

  { return X value in degrees }
  if IMaxValuesCount>0 then
     result:=ValueIndex*(360.0/IMaxValuesCount)
  else
     result:=0;
end;

{ TPolarBarSeries }

procedure TPolarBarSeries.InternalDrawValue(ValueIndex,X,Y:Integer);
begin
  if IsPainted(ValueIndex) then
  begin
    LinePrepareCanvas(ValueIndex);
    ParentChart.Canvas.LineWithZ(CircleXCenter,CircleYCenter,X,Y,StartZ);
  end;
end;

initialization
  RegisterTeeSeries(TPolarSeries, @TeeMsg_GalleryPolar,
                                  @TeeMsg_GalleryExtended,2);
  RegisterTeeSeries(TRadarSeries, @TeeMsg_GalleryRadar,
                                  @TeeMsg_GalleryExtended,2);
  RegisterTeeSeries(TPolarBarSeries, @TeeMsg_GalleryPolarBar,
                                  @TeeMsg_GalleryExtended,2);
finalization
  UnRegisterTeeSeries([TPolarSeries,TRadarSeries,TPolarBarSeries]);
end.
