{**********************************************}
{   TWindRoseSeries                            }
{   TClockSeries                               }
{   TRoseSeries                                }
{   Copyright (c) 1998-2020 by Steema Software }
{**********************************************}
unit TeeRose;
{$I TeeDefs.inc}

interface

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

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

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

     {$ELSE}
     Graphics, ExtCtrls,
     {$ENDIF}

     {$IFDEF D9}
     Types,
     {$ENDIF}

     {$IFDEF FMX}
     FMXTee.Canvas, FMXTee.Procs, FMXTee.Engine, FMXTee.Chart,
     FMXTee.Series, FMXTee.Series.Polar,
     {$ELSE}
     TeEngine, Chart, TeCanvas, Series, TeePolar, TeeProcs,
     {$ENDIF}
     Math;

{  This unit contains three Series components:

   TWindRoseSeries --  A Polar Series displaying Wind directions.
   TClockSeries    --  A Polar Series showing a watch.
   TRoseSeries     --  A Polar Series displaying a histogram of values.
}

type
  TMultiRose=(mrNone,mrStacked);

  TRoseSeries = class(TPolarSeries)
  private
    FMultiRose : TMultiRose;

    IFirst : Integer;
    Points : TFourPoints;

    Procedure CalcPolygon(ValueIndex:Integer);
    Function InheritedMaxXValue:TChartValue;
    Function InheritedMaxYValue:TChartValue;
    Procedure SetMultiRose(const Value:TMultiRose);
    Procedure SetOtherRoses(SetOthers:Boolean);
  protected
    Procedure DoBeforeDrawChart; override;
    procedure DrawValue(ValueIndex:Integer); override;
    class Function GetEditorClass:String; override;
  public
    Constructor Create(AOwner: TComponent); override;

    Function CalcYPos(ValueIndex:Integer):Integer; override;
    Function CalcXPos(ValueIndex:Integer):Integer; override;
    Function Clicked(x,y:Integer):Integer; override;
    Function MaxXValue:TChartValue; override;
    Function MaxYValue:TChartValue; override;
    Function MinXValue:TChartValue; override;
    Function MinYValue:TChartValue; override;

    Function PointOrigin(ValueIndex:Integer; IsX:Boolean):Integer; overload;
    Function PointOrigin(ValueIndexX:Integer; ValueIndexY:Integer; IsX:Boolean; IncludeSelf:Boolean=True):Integer; overload;
  published
    property MultiRose:TMultiRose read FMultiRose write SetMultiRose default mrNone;
  end;

  TCustom2DPolarSeries=class(TCustomPolarSeries)
  protected
    Procedure GalleryChanged3D(Is3D:Boolean); override;
    Procedure PrepareForGallery; override;
  end;

  TWindRoseSeries=class(TCustom2DPolarSeries)
  private
    FMirrorAngles : Boolean;
    FMirrorLabels : Boolean;

    procedure SetMirrorAngles(const Value: Boolean);
    procedure SetMirrorLabels(const Value: Boolean);
  protected
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    Function GetCircleLabel(Const Angle:Double; Index:Integer):String; override;
    class Function GetEditorClass:String; override;
    Function GetxValue(ValueIndex:Integer):TChartValue; override;
    Procedure PrepareForGallery; override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
  public
    Constructor Create(AOwner: TComponent); override;
    procedure Assign(Source:TPersistent); override;
  published
    property Active;
    property ColorEachPoint;
    property HorizAxis;
    property SeriesColor;
    property VertAxis;

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

    property AngleLabels;

    // Deprecated. See AngleLabels property.
    property CircleLabels;  // default True;
    property CircleLabelsFont;
    property CircleLabelsInside;
    property CircleLabelsRotated;

    property CirclePen;
    property CloseCircle;
    property Labels;
    property MirrorAngles:Boolean read FMirrorAngles write SetMirrorAngles default False;
    property MirrorLabels:Boolean read FMirrorLabels write SetMirrorLabels default False;
    property Pen;
    property Pointer;
    property RadiusIncrement;
    property RadiusValues;
    property RotationAngle default 90;
    property Transparency;
    property TreatNulls;

    { events }
    property OnGetCircleLabel;
    property OnGetPointerStyle;
  end;

  TClockSeriesStyle=(cssDecimal,cssRoman);

  TClockSeries=class;

  TClockSeriesGetTimeEvent=procedure(Sender:TClockSeries; Var ATime:TDateTime) of object;

  TClockHand=class(TTeeCustomShapeBrushPen)
  private
    FArrow  : Integer;
    FHeight : Integer;
    FSize   : Integer;
    FWidth  : Integer;

    ISeries : TClockSeries;

    procedure SetArrow(Value:Integer);
    procedure SetHeight(Value:Integer);
    procedure SetSize(Value:Integer);
    procedure SetWidth(Value:Integer);
  public
    Constructor Create(const AClock:TClockSeries);

    Procedure Assign(Source:TPersistent); override;
  published
    property Arrow:Integer read FArrow write SetArrow default 50;
    property Brush;
    property Height:Integer read FHeight write SetHeight;
    property Pen;
    property Size:Integer read FSize write SetSize; // %
    property Visible;
    property Width:Integer read FWidth write SetWidth;
  end;

  TClockSeries=class(TCustom2DPolarSeries)
  private
    FCenter     : TSeriesPointer;
    FCustomTime : Boolean;
    FHours      : TClockHand;
    FMinutes    : TClockHand;
    FOnGetTime  : TClockSeriesGetTimeEvent;
    FSeconds    : TClockHand;
    FStyle      : TClockSeriesStyle;
    FTime       : TDateTime;

    OldAxesVisible : Boolean;
    ITimer      : TTimer;

    procedure CustomizeChart;
    function GetPenHours: TChartPen;
    function GetPenMinutes: TChartPen;
    function GetPenSeconds: TChartPen;
    procedure ResetAxes;
    procedure SetCenter(const Value: TSeriesPointer);
    procedure SetHours(const Value: TClockHand);
    procedure SetMinutes(const Value: TClockHand);
    Procedure SetPenHours(Value:TChartPen);
    Procedure SetPenMinutes(Value:TChartPen);
    Procedure SetPenSeconds(Value:TChartPen);
    procedure SetSeconds(const Value: TClockHand);
    Procedure SetStyle(Value:TClockSeriesStyle);
    Procedure TimerExpired(Sender:TObject);
    function IsTimeStored: Boolean;
    procedure SetCustomTime(const Value: Boolean);
    procedure SetTime(const Value: TDateTime);
  protected
    Procedure AssignFormatting(Source:TChartSeries); override;
    procedure DoBeforeDrawValues; override;
    Procedure DrawAllValues; override;
    Function GetCircleLabel(Const Angle:Double; Index:Integer):String; override;
    class Function GetEditorClass:String; override;
    Procedure PrepareForGallery; override;
    Procedure SetActive(Value:Boolean); override;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
  public
    Constructor Create(AOwner: TComponent); override;
    Destructor Destroy; override;

    Function ClockTime:TDateTime;
    Function CountLegendItems:Integer; override;
    Function LegendItemColor(LegendIndex:Integer):TColor; override;
    Function LegendString( LegendIndex:Integer;
                           LegendTextStyle:TLegendTextStyle ):String; override;
    Function NumSampleValues:Integer; override;
    property Timer:TTimer read ITimer;
  published
    property Active;
    property Brush;
    property Center:TSeriesPointer read FCenter write SetCenter;
    property CircleBackColor;
    property CircleGradient;
    property Circled default True;

    // Deprecated. See AngleLabels property.
    property CircleLabels; // default True;
    property CircleLabelsFont;
    property CircleLabelsInside;
    property CircleLabelsRotated;

    property AngleLabels;
    property CirclePen;
    property CustomTime:Boolean read FCustomTime write SetCustomTime default False;
    property Labels;

    property Hours:TClockHand read FHours write SetHours;
    property Minutes:TClockHand read FMinutes write SetMinutes;
    property Seconds:TClockHand read FSeconds write SetSeconds;

    // Deprecated:
    property PenHours:TChartPen read GetPenHours write SetPenHours;
    property PenMinutes:TChartPen read GetPenMinutes write SetPenMinutes;
    property PenSeconds:TChartPen read GetPenSeconds write SetPenSeconds;

    property RotationAngle default 90;
    property ShowInLegend default False;
    property Style:TClockSeriesStyle read FStyle write SetStyle
                                     default cssRoman;

    property Time:TDateTime read ClockTime write SetTime stored IsTimeStored;

    property Transparency;

    { events }
    property OnGetCircleLabel;
    property OnGetTime  : TClockSeriesGetTimeEvent read FOnGetTime
                                                   write FOnGetTime;
  end
  {$IFDEF D11}deprecated 'Use TClockGauge series'{$ENDIF};

implementation

uses
  {$IFDEF FMX}
  FMXTee.ProConstants
  {$ELSE}
  TeeProCo
  {$ENDIF};

{ TCustom2DPolarSeries }

Procedure TCustom2DPolarSeries.GalleryChanged3D(Is3D:Boolean);
begin
  inherited;
  ParentChart.View3D:=False;
end;

Procedure TCustom2DPolarSeries.PrepareForGallery;
begin
  inherited;
  CircleLabelsFont.Size:=6;
  Pointer.HorizSize:=2;
  Pointer.VertSize:=2;
end;

{ TWindRoseSeries }
Constructor TWindRoseSeries.Create(AOwner: TComponent);
begin
  inherited;
  AngleLabels.Visible:=True;
  RotationAngle:=90;
end;

// Alternate method
(*
 function DegreesToWindDir(const Angle: Double): String;
 const WindDirs: Array[0..15] of String =
  ('N','NNW','NW','WNW','W','WSW','SW','SSW','S','SSE','SE','ESE','E','ENE','NE','NNE' );
 var i : Integer;
 begin
   i:=Trunc((Angle + 11.25) / (360/16));
   result := WindDirs[i];
 end;
*)

// Return the string corresponding to the "Angle" degree parameter
Function TWindRoseSeries.GetCircleLabel(Const Angle:Double; Index:Integer):String;
var tmp : Double;
begin
  if FMirrorLabels then tmp:=360-Angle
                   else tmp:=Angle;

  Case Round(tmp) of
      0: result:='N';
     15: result:='NNNW';
     30: result:='NNW';
     45: result:='NW';
     60: result:='NWW';
     75: result:='NWWW';
     90: result:='W';
    105: result:='SWWW';
    120: result:='SWW';
    135: result:='SW';
    150: result:='SSW';
    165: result:='SSSW';
    180: result:='S';
    195: result:='SSSE';
    210: result:='SSE';
    225: result:='SE';
    240: result:='SEE';
    255: result:='SEEE';
    270: result:='E';
    285: result:='NEEE';
    300: result:='NEE';
    315: result:='NE';
    330: result:='NNE';
    345: result:='NNNE';
    360: result:='N';
  else
    if tmp=22.5 then result:='NNW' else
    if tmp=67.5 then result:='WNW' else
    if tmp=112.5 then result:='WSW' else
    if tmp=157.5 then result:='SSW' else
    if tmp=202.5 then result:='SSE' else
    if tmp=247.5 then result:='ESE' else
    if tmp=292.5 then result:='ENE' else
    if tmp=337.5 then result:='NNE' else
       result:='';
  end;
end;

class function TWindRoseSeries.GetEditorClass: String;
begin
  result:='TWindRoseEditor';
end;

procedure TWindRoseSeries.PrepareForGallery;
begin
  inherited;
  AngleIncrement:=45;
end;

procedure TWindRoseSeries.SetMirrorAngles(const Value: Boolean);
begin
  SetBooleanProperty(FMirrorAngles,Value);
end;

procedure TWindRoseSeries.SetMirrorLabels(const Value: Boolean);
begin
  SetBooleanProperty(FMirrorLabels,Value);
end;

Function TWindRoseSeries.GetxValue(ValueIndex:Integer):TChartValue;
begin
  result:=inherited GetxValue(ValueIndex);

  if FMirrorAngles then
     result:=360-result;
end;

procedure TWindRoseSeries.Assign(Source: TPersistent);
begin
  if Source is TWindRoseSeries then
  with TWindRoseSeries(Source) do
  begin
    Self.FMirrorAngles:=FMirrorAngles;
    Self.FMirrorLabels:=FMirrorLabels;
  end;

  inherited;
end;

class procedure TWindRoseSeries.CreateSubGallery(
  AddSubChart: TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_Mirrored);
end;

class procedure TWindRoseSeries.SetSubGallery(ASeries: TChartSeries;
  Index: Integer);
begin
  inherited;
  if Index=14 then
     TWindRoseSeries(ASeries).MirrorAngles:=True;
end;

{$IFDEF D6}
{$WARN SYMBOL_DEPRECATED OFF}
{$ENDIF}

{ TClockHand }

Constructor TClockHand.Create(const AClock: TClockSeries);
begin
  inherited Create;
  FArrow:=50;
  ISeries:=AClock;
end;

Procedure TClockHand.Assign(Source:TPersistent);
begin
  if Source is TClockHand then
  with TClockHand(Source) do
  begin
    Self.FArrow:=FArrow;
    Self.FHeight:=FHeight;
    Self.FSize:=FSize;
    Self.FWidth:=FWidth;
  end;

  inherited;
end;

procedure TClockHand.SetArrow(Value:Integer);
begin
  ISeries.SetIntegerProperty(FArrow,Value);
end;

procedure TClockHand.SetHeight(Value: Integer);
begin
  ISeries.SetIntegerProperty(FHeight,Value);
end;

procedure TClockHand.SetSize(Value: Integer);
begin
  ISeries.SetIntegerProperty(FSize,Value);
end;

procedure TClockHand.SetWidth(Value: Integer);
begin
  ISeries.SetIntegerProperty(FWidth,Value);
end;

{$IFDEF D6}
{$IFNDEF LCL}
{$WARN SYMBOL_DEPRECATED DEFAULT}
{$ENDIF}
{$ENDIF}

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

  OldAxesVisible:=True;
  Pointer.Hide;
  FStyle:=cssRoman;
  Brush.Style:=bsSolid;
  AngleLabels.Visible:=True;
  RotationAngle:=90;
  Circled:=True;

  Add(0);

  FHours:=TClockHand.Create(Self);
  FHours.Width:=14;
  FHours.Height:=20;
  FHours.Size:=30; // %

  FMinutes:=TClockHand.Create(Self);
  FMinutes.Width:=10;
  FMinutes.Height:=16;
  FMinutes.Size:=70; // %

  FSeconds:=TClockHand.Create(Self);
  FSeconds.Width:=0;
  FSeconds.Height:=0;
  FSeconds.Size:=80; // %

  FCenter:=TSeriesPointer.Create(Self);
  FCenter.Style:=psCircle;

  ITimer:=TTimer.Create(Self);
  With ITimer do
  begin
    Interval:=1000;
    Enabled:=True;
    OnTimer:=TimerExpired;
  end;
end;

Destructor TClockSeries.Destroy;
begin
  FreeAndNil(FCenter);
  FreeAndNil(FHours);
  FreeAndNil(FMinutes);
  FreeAndNil(FSeconds);

  ITimer.Free;
  inherited;
end;

Procedure TClockSeries.TimerExpired(Sender:TObject);
begin
  ITimer.Enabled:=False;
  Repaint;
  ITimer.Enabled:=Visible;
end;

Procedure TClockSeries.AssignFormatting(Source:TChartSeries);
begin
  if Source is TClockSeries then
  With TClockSeries(Source) do
  begin
    Self.Hours   :=FHours;
    Self.Minutes :=FMinutes;
    Self.Seconds :=FSeconds;
    Self.FStyle  :=FStyle;
    Self.Center  :=FCenter;
  end;

  inherited;
end;

Function TClockSeries.ClockTime:TDateTime;
begin
  if not FCustomTime then
     FTime:=Now;

  result:=FTime;

  if Assigned(FOnGetTime) then
     FOnGetTime(Self,result);
end;

Procedure TClockSeries.DrawAllValues;
Var X : Integer;
    Y : Integer;

  Procedure CalcPos(Const Angle,ASize:Single);
  begin
    AngleToPos(Angle*TeePiStep,ASize*XRadius*0.5,ASize*YRadius*0.5,X,Y);
  end;

  procedure TryDrawHand(const Hand:TClockHand; const Angle:Single);
  begin
    if Hand.Visible then
    begin
      CalcPos(Angle,1+(0.01*Hand.Size));

      with ParentChart.Canvas do
      begin
        AssignBrush(Hand.Brush);
        AssignVisiblePen(Hand.Pen);
        Arrow(True,Hand.Arrow,TeePoint(CircleXCenter,CircleYCenter),TeePoint(X,Y),Hand.Width,Hand.Height);
      end;
    end;
  end;

Var H  : Word;
    M  : Word;
    S  : Word;
    Ms : Word;
    tmp : TDateTime;
    tmpBlend : TTeeBlend;
begin
  if Transparency>0 then
     tmpBlend:=ParentChart.Canvas.BeginBlending(ParentChart.ChartRect,Transparency)
  else
     tmpBlend:=nil;

  tmp:=ClockTime;
  DecodeTime(tmp,H,M,S,Ms);

  TryDrawHand(FHours,360.0-(360.0*(60.0*H+M)/(12.0*60.0)));
  TryDrawHand(FMinutes,360.0-(360.0*M/60.0));

  tmp:=360.0-(360.0*(S+(Ms/1000.0))/60.0);

  TryDrawHand(FSeconds,tmp);

  with Pointer do
  if Visible then
  begin
    PrepareCanvas(ParentChart.Canvas,Color);
    CalcPos(tmp,1+(0.01*FSeconds.Size));
    Draw(X,Y);
  end;

  with Center do
  if Visible then
  begin
    PrepareCanvas(ParentChart.Canvas,Color);
    Draw(CircleXCenter,CircleYCenter);
  end;

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

procedure TClockSeries.ResetAxes;
begin
  if Assigned(ParentChart) then
     ParentChart.Axes.Visible:=OldAxesVisible;
end;

Procedure TClockSeries.SetParentChart(Const Value:TCustomAxisPanel);
var tmpDif : Boolean;
begin
  tmpDif:=(ParentChart<>Value);

  if tmpDif and (not Assigned(Value)) then
     ResetAxes;

  inherited;

  if Assigned(FHours) then
     FHours.ParentChart:=ParentChart;

  if Assigned(FMinutes) then
     FMinutes.ParentChart:=ParentChart;

  if Assigned(FSeconds) then
     FSeconds.ParentChart:=ParentChart;

  if Assigned(FCenter) then
     FCenter.ParentChart:=ParentChart;

  if tmpDif and Assigned(ParentChart) and Visible then
  begin
    OldAxesVisible:=Value.Axes.Visible;
    CustomizeChart;
  end;
end;

procedure TClockSeries.CustomizeChart;
begin
  ParentChart.Axes.Visible:=False;
  AngleIncrement:=30;
end;

Procedure TClockSeries.SetActive(Value:Boolean);
Begin
  inherited;

  if Active then
  begin
    if Assigned(ParentChart) then
       CustomizeChart;
  end
  else
     ResetAxes;

  ITimer.Enabled:=Visible;
end;

{ Return the string corresponding to the "Angle" degree parameter }
Function TClockSeries.GetCircleLabel(Const Angle:Double; Index:Integer):String;
Const RomanNumber:Array[1..12] of String=
  ('I','II','III','IV','V','VI','VII','VIII','IX','X','XI','XII');
var tmpAngle : Integer;
    {$IFNDEF D18}
    tmpS     : ShortString;
    {$ENDIF}
begin
  tmpAngle:=Round((360.0-Angle)/30.0);

  if FStyle=cssDecimal then
  begin
     {$IFDEF D18}
     result:=IntToStr(tmpAngle);
     {$ELSE}
     Str(tmpAngle,tmpS);
     result:=String(tmpS);
     {$ENDIF}
  end
  else
  begin
    if tmpAngle=0 then
       tmpAngle:=12;

    result:=RomanNumber[tmpAngle];
  end;
end;

Procedure TClockSeries.SetPenHours(Value:TChartPen);
begin
  FHours.Pen:=Value;
end;

Procedure TClockSeries.SetPenMinutes(Value:TChartPen);
begin
  FMinutes.Pen:=Value;
end;

Procedure TClockSeries.SetPenSeconds(Value:TChartPen);
begin
  FSeconds.Pen:=Value;
end;

Procedure TClockSeries.SetStyle(Value:TClockSeriesStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;
    Repaint;
  end;
end;

Function TClockSeries.NumSampleValues:Integer;
begin
  result:=1;
end;

class function TClockSeries.GetEditorClass: String;
begin
  result:='TClockEditor';
end;

function TClockSeries.CountLegendItems: Integer;
begin
  result:=1;
end;

function TClockSeries.LegendItemColor(LegendIndex: Integer): TColor;
begin
  result:=clNone;
end;

function TClockSeries.LegendString(LegendIndex: Integer;
  LegendTextStyle: TLegendTextStyle): String;
begin
  result:=TimeToStr(ClockTime);
end;

function TClockSeries.GetPenHours: TChartPen;
begin
  result:=FHours.Pen;
end;

function TClockSeries.GetPenMinutes: TChartPen;
begin
  result:=FMinutes.Pen;
end;

function TClockSeries.GetPenSeconds: TChartPen;
begin
  result:=FSeconds.Pen;
end;

procedure TClockSeries.SetCenter(const Value: TSeriesPointer);
begin
  FCenter.Assign(Value);
end;

procedure TClockSeries.SetHours(const Value: TClockHand);
begin
  FHours.Assign(Value);
end;

procedure TClockSeries.SetMinutes(const Value: TClockHand);
begin
  FMinutes.Assign(Value);
end;

procedure TClockSeries.SetSeconds(const Value: TClockHand);
begin
  FSeconds.Assign(Value);
end;

procedure TClockSeries.DoBeforeDrawValues;
var tmpBlend : TTeeBlend;
begin
  if Transparency>0 then
     tmpBlend:=ParentChart.Canvas.BeginBlending(ParentChart.ChartRect,Transparency)
  else
     tmpBlend:=nil;

  inherited;

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

procedure TClockSeries.PrepareForGallery;
begin
  inherited;
  Brush.Gradient.Visible:=True;
end;

function TClockSeries.IsTimeStored: Boolean;
begin
  result:=FCustomTime;
end;

procedure TClockSeries.SetCustomTime(const Value: Boolean);
begin
  SetBooleanProperty(FCustomTime,Value);

  if FCustomTime then
     ITimer.Enabled:=False
  else
  if Visible then
     ITimer.Enabled:=True;
end;

procedure TClockSeries.SetTime(const Value: TDateTime);
begin
  FCustomTime:=True;
  FTime:=Value;
  Repaint;
end;

{ TRoseSeries }

Constructor TRoseSeries.Create(AOwner: TComponent);
begin
  inherited;
  Brush.Style:=bsSolid;
end;

function TRoseSeries.CalcXPos(ValueIndex: Integer): Integer;
Begin
  if (FMultiRose=mrNone) then
     result:=inherited CalcXPos(ValueIndex)
  else
  begin
     result:=PointOrigin(ValueIndex, true);
  end;
End;

function TRoseSeries.CalcYPos(ValueIndex: Integer): Integer;
Begin
  if (FMultiRose=mrNone) then
    result:=inherited CalcYPos(ValueIndex)
  else
  begin
    result:=PointOrigin(ValueIndex, false);
  end;
End;

function TRoseSeries.Clicked(x, y: Integer): Integer;
var t : Integer;
begin
  result:=-1;

  if (FirstValueIndex>-1) and (LastValueIndex>-1) then
     for t:=FirstValueIndex to LastValueIndex do
         if IsPainted(t) then
         begin
           CalcPolygon(t);

           if PointInPolygon(TeePoint(X,Y),Points) then
           begin
             Result:=t;
             exit;
           end
         end;
end;

Procedure TRoseSeries.DoBeforeDrawChart;
var t : Integer;
begin
  inherited;

  for t:=0 to ParentChart.SeriesCount-1 do
  if ParentChart.Series[t].SameClass(Self) and ParentChart.Series[t].Active then
  begin
    IFirst:=t;
    Exit;
  end;

  IFirst:=-1;
end;

procedure TRoseSeries.CalcPolygon(ValueIndex: Integer);

  procedure CalcPoint(AIndex:Integer; out AX,AY:Integer);
  begin
    AX:=PointOrigin(AIndex, ValueIndex, True, False);
    AY:=PointOrigin(AIndex, ValueIndex, False, False);
  end;

var X : Integer;
    Y : Integer;
    StartX0, StartY0,
    StartX1, StartY1 : Integer;
begin
  if Count > 1 then
  begin
    OldX:=0;
    OldY:=0;

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

    if ValueIndex < Count - 1 then
    begin
      OldX:=PointOrigin(ValueIndex + 1, ValueIndex, True);
      OldY:=PointOrigin(ValueIndex + 1, ValueIndex, False);
    end
    else
    begin
      OldX:=PointOrigin(FirstValueIndex, ValueIndex, True);
      OldY:=PointOrigin(FirstValueIndex, ValueIndex, False);
    end;

    if ((MultiRose=mrStacked) and (SeriesIndex=IFirst))
       or (MultiRose=mrNone) then
    begin
      StartX0:=CircleXCenter;
      StartY0:=CircleYCenter;
      StartX1:=CircleXCenter;
      StartY1:=CircleYCenter;
    end
    else
    begin
      CalcPoint(ValueIndex, StartX0, StartY0);

      if ValueIndex < Count - 1 then
         CalcPoint(ValueIndex+1,StartX1,StartY1)
      else
         CalcPoint(FirstValueIndex,StartX1,StartY1);
    end;

    Points[0]:=TeePoint(StartX0, StartY0);
    Points[1]:=TeePoint(X, Y);
    Points[2]:=TeePoint(OldX, OldY);
    Points[3]:=TeePoint(StartX1, StartY1);
  end;
end;

procedure TRoseSeries.DrawValue(ValueIndex: Integer);

  function PrepareBlend:TTeeBlend;
  var R : TRect;
  begin
    if Transparency>0 then
    begin
      if ParentChart.Canvas.SupportsAlpha then
         result:=ParentChart.Canvas.BeginBlending(TeeZeroRect,Transparency)
      else
      begin
        R.Left  :=Math.Min(CircleXCenter,Math.Min(Points[2].X,Points[1].X));
        R.Top   :=Math.Min(CircleYCenter,Math.Min(Points[2].Y,Points[1].Y));
        R.Right :=Math.Max(CircleXCenter,Math.Max(Points[2].X,Points[1].X));
        R.Bottom:=Math.Max(CircleYCenter,Math.Max(Points[2].Y,Points[1].Y));

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

        result:=ParentChart.Canvas.BeginBlending(R,Transparency);
      end;
    end
    else
       result:=nil;
  end;

var tmpBlend : TTeeBlend;
begin
  if IsPainted(ValueIndex) then
  begin
    if not PrepareHoverPen(ValueIndex) then
       ParentChart.Canvas.AssignVisiblePen(Pen);

    ParentChart.SetBrushCanvas(CalcValueColor(ValueIndex),Brush,Brush.Color);

    CalcPolygon(ValueIndex);

    tmpBlend:=PrepareBlend;

    ParentChart.Canvas.Polygon(Points);

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

class function TRoseSeries.GetEditorClass: String;
begin
  result:='TRoseSeriesEditor'; { <-- dont translate ! }
end;

function TRoseSeries.MaxXValue: TChartValue;
var t : Integer;
    tmpSeries : TChartSeries;
begin
  if MultiRose=mrNone then
     result:=InheritedMaxXValue
  else
  begin
    result:=0;

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

        with tmpSeries do
        if Active and SameClass(Self) then
           result:=result+TRoseSeries(tmpSeries).InheritedMaxXValue;
      end;
  end;
end;

function TRoseSeries.MaxYValue: TChartValue;
var t : Integer;
    tmpSeries : TChartSeries;
begin
  if MultiRose=mrNone then
     result:=InheritedMaxYValue
  else
  begin
    result:=0;

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

        with tmpSeries do
        if Active and SameClass(Self) then
           result:=result+TRoseSeries(tmpSeries).InheritedMaxYValue;
      end;
  end;
end;

function TRoseSeries.MinXValue: TChartValue;
begin
  if MultiRose=mrNone then
     result:=inherited MinXValue
  else
     result:=inherited MinXValue
end;

function TRoseSeries.MinYValue: TChartValue;
begin
  if MultiRose=mrNone then
     result:=inherited MinYValue
  else
     result:=inherited MinYValue
end;

function TRoseSeries.PointOrigin(ValueIndex: Integer; IsX: Boolean): Integer;
begin
  result:=PointOrigin(ValueIndex, ValueIndex, IsX);
end;

Function TRoseSeries.InheritedMaxXValue:TChartValue;
begin
  result:=inherited MaxXValue;
end;

Function TRoseSeries.InheritedMaxYValue:TChartValue;
begin
  result:=inherited MaxYValue;
end;

procedure TRoseSeries.SetMultiRose(const Value: TMultiRose);
begin
  if Value<>FMultiRose then
  Begin
    FMultiRose:=Value;
    SetOtherRoses(True);
    Repaint;
  end;
end;

procedure TRoseSeries.SetOtherRoses(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 TRoseSeries(Series[t]) do
         if SetOthers then
            FMultiRose:=Self.FMultiRose
         else
         begin
           Self.FMultiRose:=FMultiRose;
           break;
         end;
end;

function TRoseSeries.PointOrigin(ValueIndexX, ValueIndexY: Integer; IsX,
                                 IncludeSelf: Boolean): Integer;

  function InternalPointOrigin:TChartValue;
  var tmpSeries : TChartSeries;
      t, tmpIndex : Integer;
  begin
    result:=0;

    if Assigned(ParentChart) then
    with ParentChart do
    begin
      tmpIndex:=Self.SeriesIndex;

      for t:=0 to tmpIndex do
      Begin
        tmpSeries:=Series[t];

        if tmpSeries.Active and tmpSeries.SameClass(Self) and (tmpSeries.Count>ValueIndexX) then
           if ((tmpSeries=Self) and IncludeSelf) or not (tmpSeries=Self) then
              result:=result+tmpSeries.YValues[ValueIndexY];
      end;
    end;
  end;

var tmp : Integer;
    tmpValue : Double;
begin
  if FMultiRose=mrStacked then
     tmpValue:=InternalPointOrigin
  else
     tmpValue:=YValues[ValueIndexY];

  if IsX then
     CalcXYPosition(AngleValues[ValueIndexX],tmpValue,XRadius,GetHorizAxis,result,tmp)
  else
     CalcXYPosition(AngleValues[ValueIndexX],tmpValue,YRadius,GetVertAxis,tmp,result);
end;

initialization
  RegisterTeeSeries( TWindRoseSeries, @TeeMsg_GalleryWindRose,
                                      @TeeMsg_GallerySamples, 1 );

  // TClockSeries has been deprecated.
  // Use TClockGauge series class, in TeeCircularGauge.pas unit
  {$IFDEF D6}
  {$WARN SYMBOL_DEPRECATED OFF}
  {$ENDIF}

  Classes.RegisterClass(TClockSeries);

  {$IFDEF D6}
  {$IFNDEF LCL}
  {$WARN SYMBOL_DEPRECATED DEFAULT}
  {$ENDIF}
  {$ENDIF}

  RegisterTeeSeries( TRoseSeries, @TeeMsg_GalleryRose,
                                  @TeeMsg_GallerySamples, 1 );
finalization
  UnRegisterTeeSeries([ TWindRoseSeries, TRoseSeries ]);
end.

