{**********************************************}
{   TeeChart Numeric Gauge style               }
{   Copyright (c) 2007-2020 by Steema Software }
{**********************************************}
unit TeeNumericGauge;
{$I TeeDefs.inc}

interface

uses
  {$IFNDEF LINUX}
  {$IFNDEF FMX}
  Windows, Messages,
  {$ENDIF}
  {$ENDIF}
  SysUtils, Classes, Math,
  {$IFDEF D6}
  Types, StrUtils,
  {$ENDIF}
    
  {$IFDEF D17}
  System.UIConsts, System.UITypes,
  {$ENDIF}

  {$IFDEF FMX}
  FMX.Types,
  {$ELSE}
  Graphics,
  {$ENDIF}

  {$IFDEF FMX}
  FMXTee.Procs, FMXTee.Engine, FMXTee.Chart, FMXTee.Canvas, FMXTee.Tools,
  FMXTee.Series, FMXTee.Animate
  {$ELSE}
  TeeProcs, TeEngine, Chart, TeCanvas, TeeTools, Series, TeeAnimate
  {$ENDIF}
  ;

const
  TeeGauge_DefaultValueFormat='#,##0.00#';

type
  TNumericGauge=class; // forward

  TNumericShape=class(TTextShape)
  private
    function CalcWidth(const Panel:TCustomTeePanel; const AText:String):Integer;
    function IsElektra:Boolean;
    function IsDigital:Boolean;
    procedure CalcVariables(const ACanvas:TTeeCanvas; out AWidth, AHeight, AComma, AQuote: Integer);
  protected
    procedure CalcTextAlign(const Panel:TCustomTeePanel; out X: TCoordinate;
                            const R: TRect; AText:String); override;

    function CalcTextSize(const Panel:TCustomTeePanel; S:String;
                          const TextFormat:TTextFormat; out NumLines:Integer):TPoint; override;

    procedure DrawString(const Panel:TCustomTeePanel;
                         const X:TCoordinate;
                         const Y,Z:Integer;
                         const Text:String;
                         AllowHTML:Boolean;
                         AAngle:Integer;
                         const RotationCenter:TPoint); override;
  end;

  TNumericMarker=class(TAnnotationTool)
  protected
    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}FGauge : TNumericGauge;

    function CreateShape:TTextShape; override;
    procedure SetActive(Value:Boolean); override;
  public
    Destructor Destroy; override;

    property Gauge:TNumericGauge read FGauge;
  end;

  TNumericMarkers=class(TChartTools)
  private
    {$IFDEF AUTOREFCOUNT}[weak]{$ENDIF}FGauge : TNumericGauge;

    function Get(Index: Integer): TNumericMarker;
    procedure Put(Index: Integer; const Value: TNumericMarker);
    procedure SetParentChart(const Value:TCustomTeePanel);
  public
    function Add(const AText:String):TNumericMarker; overload;
    function Add(const AText:String; FontSize:Integer; APosition:TAnnotationPosition;
                 AAlignment:TAlignment;FontColor,ShapeColor:Integer):TNumericMarker; overload;
    procedure Assign(Source:TNumericMarkers); overload;

    property Gauge:TNumericGauge read FGauge;
    property Items[Index:Integer]:TNumericMarker read Get write Put; default;
  end;

  TCustomGauge=class(TChartSeries)
  private
    FFace         : TTeeShape;
    FFrame        : TFramedBorder;
    FOnChange     : TNotifyEvent;

    FCustomBounds : TRect;

    function GetFrame: TFramedBorder;
    function GetValue: TChartValue;
    procedure SetFace(const Value: TTeeShape);
    procedure SetFrame(const Value: TFramedBorder);
  protected
    IAnimation : TNumberAnimation;
    ICircled  : Boolean;
    INewRect  : TRect;
    IOrigRect : TRect;
    IGalleryStatic : Boolean;

    procedure CalcOrigRect; virtual;
    function CreateFrame:TFramedBorder; virtual;
    procedure DrawAllValues; override;
    procedure DrawFace; virtual;
    procedure GalleryChanged3D(Is3D: Boolean); override;
    procedure PrepareForGallery; override;
    procedure SetGaugePalette(const Palette:Array of TColor); virtual;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    procedure SetValue(const AValue: TChartValue); virtual;
    procedure SetValues; virtual;
  public
    GaugeColorPalette : TColorArray;

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

    procedure Assign(Source:TPersistent); override;

    class function CreateAnimation(const AOwner:TComponent;
                                   const StartValue,EndValue:Double;
                                   Duration:Integer):TNumberAnimation;

    function Clicked(x,y:Integer):Integer; override;
    function GetPaletteColor(Index:Integer):TColor;

    property CustomBounds:TRect read FCustomBounds write FCustomBounds;

    property Face:TTeeShape read FFace write SetFace;
    property Frame:TFramedBorder read GetFrame write SetFrame;
    property Value:TChartValue read GetValue write SetValue;
    property OnChange:TNotifyEvent read FOnChange write FOnChange;
  end;

  TDigitalFont=(dfBar, dfDot, dfCustom);

  TNumericGauge=class(TCustomGauge)
  private
    FFont    : TDigitalFont;
    FMarkers : TNumericMarkers;

    procedure ChangeValueMarker;
    function GetTextMarker: TNumericMarker;
    function GetUnitsMarker: TNumericMarker;
    function GetValueMarker: TNumericMarker;
    function IsValueFormatStored:Boolean;
    procedure SetFont(const Value: TDigitalFont);
    procedure SetMarkers(const Value: TNumericMarkers);
  protected
    Procedure AddSampleValues(NumValues:Integer; Sequential:Boolean=False); override;
    class Procedure CreateSubGallery(AddSubChart:TChartSubGalleryProc); override;
    procedure DefineProperties(Filer: TFiler); override;
    procedure DrawAllValues; override;
    class Function GetEditorClass:String; override;
    Procedure PrepareForGallery; override;
    procedure ReadMarkers(Stream: TStream);
    procedure SetGaugePalette(const Palette:Array of TColor); override;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
    class Procedure SetSubGallery(ASeries:TChartSeries; Index:Integer); override;
    procedure SetValue(const Value:TChartValue); override;
    Procedure SetValueFormat(Const Value:String); override;
    procedure WriteMarkers(Stream: TStream);
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    procedure Assign(Source:TPersistent); override;


    function Clicked(x,y:Integer):Integer; override;

    Function NumSampleValues:Integer; override;

    function UseAxis:Boolean; override;

    property Embedded:Boolean read IInternalUse write IInternalUse;
    property Markers:TNumericMarkers read FMarkers write SetMarkers;
    property TextMarker:TNumericMarker read GetTextMarker;
    property UnitsMarker:TNumericMarker read GetUnitsMarker;
    property ValueMarker:TNumericMarker read GetValueMarker;
  published
    property Active;
    property Cursor;
    property ParentChart;
    property DataSource;  { after parentchart }
    property PercentFormat;
    property ShowInLegend;
    property Title;
    property ValueFormat stored IsValueFormatStored;

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

    property XLabelsSource;
    property XValues;
    property YValues;

    property DigitalFont:TDigitalFont read FFont write SetFont default dfBar;
    property Face;
    property Frame;
    property Value;
  end;

const
  LCDPalette : Array[0..23] of TColor=
  (
    $282828,
    $323232,
    $646464,
    $82AAAA,
    $C8DBDB,
    $82AAAA,
    $282864,
    $000000,
    $646464,
    $646464,
    $000000,
    $1EC81E,
    $3C73C8,
    $3C73C8,
    $3C73C8,
    $0A3296,
    $828282,
    $828282,
    $000000,
    $1E1E1E,
    $282828,
    $646464,
    $646464,
    $646464
    );


  LEDPalette : Array[0..23] of TColor=
  (
    $7D3705,
    $C8780A,
    $A05A05,
    $0A0A0A,
    $0A0A0A,
    $0A0A0A,
    $282864,
    $000000,
    $646464,
    $646464,
    $000000,
    $1EC81E,
    $3C73C8,
    $3C73C8,
    $3C73C8,
    $0A3296,
    $828282,
    $828282,
    $000000,
    $1E1E1E,
    $1E1EFF,
    $646464,
    $646464,
    $646464
    );

implementation

uses
  {$IFDEF FMX}
  FMXTee.Constants, FMXTee.ProConstants
  {$ELSE}

  {$IFDEF LCL}
  LCLType,
  {$ENDIF}

  TeeConst, TeeProCo
  {$ENDIF};

const
  BarFontSpace=1;
  DotFontSpace=1;
  ITextWidth=1; //.375;

{ TNumericGauge }

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

  FMarkers:=TNumericMarkers.Create;
  FMarkers.FGauge:=Self;

  ValueFormat:=TeeGauge_DefaultValueFormat;

  SetGaugePalette(LCDPalette);

  if (not Assigned(Owner)) or (not (csLoading in Owner.ComponentState)) then
  begin
    FMarkers.Add(FormatFloat(ValueFormat,Value),36,ppCenter,taRightJustify,20,4);
    FMarkers.Add('MHz',18,ppLeftBottom,taLeftJustify,20,4);
    FMarkers.Add('FREQ',11,ppLeftTop,taLeftJustify,4,20).Shape.Transparent:=False;
  end;
end;

Destructor TNumericGauge.Destroy;
begin
  FMarkers.Clear;
  FMarkers.Free;

  inherited;
end;

class function TNumericGauge.GetEditorClass: String;
begin
  result:='TNumericGaugeEditor';
end;

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

procedure TNumericGauge.SetFont(const Value: TDigitalFont);
var t : Integer;
    tmp : String;
begin
  if FFont<>Value then
  begin
    FFont:=Value;

    if FFont<>dfCustom then
    begin
      if FFont=dfBar then
         tmp:=TeeFontDigital
      else
         tmp:=TeeFontElektra;

      for t:=0 to Markers.Count-1 do
          Markers[t].Shape.Font.Name:=tmp;
    end;

    Repaint;
  end;
end;

class procedure TNumericGauge.CreateSubGallery(
  AddSubChart: TChartSubGalleryProc);
begin
  inherited;
  AddSubChart(TeeMsg_LEDGauge);
end;

procedure TNumericGauge.DefineProperties(Filer: TFiler);
begin
  inherited;
  Filer.DefineBinaryProperty('InternalMarkers',ReadMarkers,WriteMarkers,Markers.Count>0);
end;

procedure TNumericGauge.SetGaugePalette(const Palette:Array of TColor);

  procedure GetPalette(const Marker:TNumericMarker; FontColor,ShapeColor:Integer);
  begin
    if Assigned(Marker) then
    with Marker.Shape do
    begin
      Font.Color:=GetPaletteColor(FontColor);
      Color:=GetPaletteColor(ShapeColor);
    end;
  end;

begin
  inherited;

  if Markers.Count>0 then
     GetPalette(ValueMarker,20,4);

  if Markers.Count>1 then
     GetPalette(UnitsMarker,20,4);

  if Markers.Count>2 then
     GetPalette(TextMarker,4,20);
end;

class procedure TNumericGauge.SetSubGallery(ASeries: TChartSeries;
  Index: Integer);
begin
  inherited;

  if Index=1 then
  with ASeries as TNumericGauge do
  begin
    DigitalFont:=dfDot;
    SetGaugePalette(LEDPalette);
  end;
end;

procedure TNumericGauge.DrawAllValues;
var t    : Integer;
    tmpR : TRect;
    tmpX,
    tmpY : Integer;
begin
  inherited;

  for t:=0 to Markers.Count-1 do
  with Markers[t] do
  if Active and Shape.Visible then
  begin
    tmpR:=GetTextBounds(Self.ParentChart,INewRect,tmpX,tmpY);
    Shape.Draw(Self.ParentChart,tmpR,0,Z);
  end;
end;

procedure TCustomGauge.CalcOrigRect;

  procedure ApplyShadow(const AShadow:TTeeShadow; const AFactor:Integer);
  begin
    with AShadow do
    if Visible then
    begin
      if AFactor*HorizSize>0 then
         IOrigRect.Right:=IOrigRect.Right-HorizSize
      else
         IOrigRect.Left:=IOrigRect.Left+HorizSize;

      if AFactor*VertSize>0 then
         IOrigRect.Bottom:=IOrigRect.Bottom-VertSize
      else
         IOrigRect.Top:=IOrigRect.Top+VertSize;
    end;
  end;

var maxHeight, maxWidth,
    height, width, x, y,
    tmpCount,
    seriesIndex,
    t,
    step : Integer;
    CanMix, Horizontal: Boolean;
begin
  if IsRectEmpty(FCustomBounds) then
  begin
    IOrigRect:=ParentChart.ChartRect;

    CanMix:=True;

    if Assigned(FFrame) and FFrame.Visible then
    begin
      ApplyShadow(FFrame.Shadow,1);
      ApplyShadow(FFrame.Emboss,-1);
    end;

    maxHeight:= {$IFDEF FMX}Round{$ENDIF}(IOrigRect.Bottom-IOrigRect.Top);
    maxWidth:= {$IFDEF FMX}Round{$ENDIF}(IOrigRect.Right-IOrigRect.Left);
    Horizontal:=maxWidth>maxHeight;

    tmpCount:=0;
    seriesIndex:=0;

    with ParentChart do
    for t:=0 to SeriesCount-1 do
      if (Series[t] is TCustomGauge) and Series[t].Visible and not TCustomGauge(Series[t]).IInternalUse then
      begin
        Inc(tmpCount);

        if not (Series[t] as TCustomGauge).ICircled then CanMix:=False;

        if Series[t]=Self then
           seriesIndex:=tmpCount-1;
      end;

    if tmpCount=0 then
       Exit;

    //If series cannot be "mixed" they will be always vertical
    if not CanMix then Horizontal:=False;

    if Horizontal then
    begin
      step:= maxWidth div tmpCount;

      height := maxHeight;
      width := Math.Min(maxWidth,step)-3;

      x := {$IFDEF FMX}Round{$ENDIF}(IOrigRect.Left);
      y := {$IFDEF FMX}Round{$ENDIF}(IOrigRect.Top);

      if tmpCount = 1 then
         Inc(x, 0)
      else
         Inc(x, step * seriesIndex);
    end
    else
    begin
      step:= maxHeight div tmpCount;

      height := Math.Min(maxHeight, step) -3;
      width := maxWidth;

      x := {$IFDEF FMX}Round{$ENDIF}(IOrigRect.Left);
      y := {$IFDEF FMX}Round{$ENDIF}(IOrigRect.Top);

      if tmpCount = 1 then
         Inc(y, 0)
      else
         Inc(y, step * seriesIndex);
    end;

    IOrigRect:=TeeRect(x,y, x+width,y+height);
  end
  else
     IOrigRect:=FCustomBounds;
end;

type
  TFramedBorderAccess=class(TFramedBorder);

function TCustomGauge.CreateFrame:TFramedBorder;
begin
  result:=TFramedBorder.Create;
  result.Visible:=False;
  TFramedBorderAccess(result).DefaultVisible:=False;
end;

function TCustomGauge.GetFrame: TFramedBorder;
begin
  if not Assigned(FFrame) then
  begin
    FFrame:=CreateFrame;
    FFrame.ParentChart:=ParentChart;
  end;

  result:=FFrame;
end;

function TCustomGauge.GetPaletteColor(Index: Integer): TColor;
var tmp : Integer;
begin
  tmp:=Length(GaugeColorPalette);

  if tmp=0 then
     if Assigned(ParentChart) then
        result:=ParentChart.GetDefaultColor(Index)
     else
        result:={$IFDEF FMX}claBlack{$ELSE}clBlack{$ENDIF}
  else
  begin
    if Index>=tmp then
       Index:=tmp-1;

    result:=GaugeColorPalette[Index];
  end;
end;

procedure TNumericGauge.Assign(Source: TPersistent);
begin
  if Source is TNumericGauge then
  with TNumericGauge(Source) do
  begin
    Self.FFont:=FFont;
    Self.Markers:=FMarkers;
  end;

  inherited;
end;

procedure TNumericGauge.SetMarkers(const Value: TNumericMarkers);
begin
  FMarkers.Assign(Value);
end;

procedure TNumericGauge.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;

  if not (csDestroying in ComponentState) then
     FMarkers.SetParentChart(ParentChart);
end;

procedure TNumericGauge.AddSampleValues(NumValues: Integer;
  Sequential: Boolean);
begin
  inherited;
  Value:=Value; // force reset text of ValueMarker annotation
end;

procedure TNumericGauge.PrepareForGallery;
begin
  inherited;

  Value:=Random(30);
  ValueMarker.Position:=ppCenter;
  ValueFormat:='0.0';

  ValueMarker.Shape.Font.Size:=18;
  UnitsMarker.Shape.Font.Size:=16;
  TextMarker.Shape.Font.Size:=12;
end;

procedure TNumericGauge.ReadMarkers(Stream: TStream);
var t   : Integer;
    Num : Integer;
    tmp : TNumericMarker;
begin
  Markers.Clear;

  Stream.Read(Num,SizeOf(Num));

  for t:=0 to Num-1 do
  begin
    tmp:=Stream.ReadComponent(nil) as TNumericMarker;
    tmp.FGauge:=Self;
    Markers.Add(tmp);
  end;

  FMarkers.SetParentChart(ParentChart);
end;

procedure TNumericGauge.WriteMarkers(Stream: TStream);
var t : Integer;
begin
  Stream.WriteBuffer(Markers.Count,SizeOf(Markers.Count));

  for t:=0 to Markers.Count-1 do
      Stream.WriteComponent(Markers[t]);
end;

function TNumericGauge.GetTextMarker: TNumericMarker;
begin
  while Markers.Count<3 do
        Markers.Add('');
        
  result:=Markers[2];
end;

function TNumericGauge.GetUnitsMarker: TNumericMarker;
begin
  while Markers.Count<2 do
        Markers.Add('');

  result:=Markers[1];
end;

function TNumericGauge.GetValueMarker: TNumericMarker;
begin
  if Markers.Count=0 then
     Markers.Add('');

  result:=Markers[0];
end;

function TNumericGauge.IsValueFormatStored:Boolean;
begin
  result:=ValueFormat<>TeeGauge_DefaultValueFormat;
end;

function TNumericGauge.Clicked(x, y: Integer): Integer;
begin
  result:=inherited Clicked(x,y);

  // Improve: Loop all Markers and return index under mouse,
  // what about back area?
end;

procedure TNumericGauge.ChangeValueMarker;
begin
  if Markers.Count>0 then
     ValueMarker.Text:=FormatFloat(ValueFormat,Value);
end;

procedure TNumericGauge.SetValue(const Value: TChartValue);
begin
  inherited;
  ChangeValueMarker;
end;

function TNumericGauge.NumSampleValues: Integer;
begin
  result:=1;
end;

procedure TNumericGauge.SetValueFormat(const Value: String);
begin
  inherited;
  ChangeValueMarker;
end;

{ TNumericMarker }

function TNumericMarker.CreateShape:TTextShape;
begin
  result:=TNumericShape.Create(nil);
end;

Destructor TNumericMarker.Destroy;
begin
  if Assigned(FGauge) then //and (not (csDestroying in FGauge.ComponentState)) then
  with FGauge.Markers do
       Delete(IndexOf(Self));

  inherited;
end;

Procedure TNumericMarker.SetActive(Value:Boolean);
begin
  inherited;
  FGauge.Repaint;
end;

(*
function TNumericMarker.TextWidth(Panel: TCustomAxisPanel;
  AText: String): Integer;
var tmpW, tmpC, t : Integer;
    c:Char;
begin
  result:=0;

  tmpW:=Round(Panel.Canvas.TextWidth('W') * ITextWidth);

  if IsDigital then tmpC:=Panel.Canvas.TextWidth(',')
               else tmpC:=0;

  for t := 1 to Length(AText) do
  begin
    c:=AText[t];
    if c='.' then
    begin
      if IsDigital then
      begin
       result:=result+tmpC;
       tmpC:=0;
      end
    end
    else if c=',' then
    begin
      if IsDigital then
      begin
        result:=result+tmpC;
        tmpC:=0;
      end
    end
    else
      result:=result+tmpW;
  end;
end;
*)

{ TNumericMarkers }

function TNumericMarkers.Add(const AText: String): TNumericMarker;
begin
  result:=inherited Add(TNumericMarker) as TNumericMarker;

  with result.Shape do
  begin
    Transparent:=True;
    Font.Name:=TeeFontDigital;
    Pen.Visible:=False;
    Emboss.Visible:=False;
    Shadow.Visible:=False;
    ParentChart:=Self.FGauge.ParentChart;
  end;

  result.FGauge:=FGauge;
  result.Text:=AText;
end;

function TNumericMarkers.Add(const AText:String; FontSize:Integer; APosition:TAnnotationPosition;
                 AAlignment:TAlignment; FontColor,ShapeColor:Integer):TNumericMarker;
begin
  result:=Add(AText);

  result.Position:=APosition;

  with result.Shape do
  begin
    Font.Size:=FontSize;
    Font.Color:=FGauge.GetPaletteColor(FontColor);
    Color:=FGauge.GetPaletteColor(ShapeColor);
    TextAlignment:=AAlignment;
  end;
end;

procedure TNumericMarkers.Assign(Source: TNumericMarkers);
var t : Integer;
begin
  Clear;

  for t:=0 to Source.Count-1 do
      Add('').Assign(Source[t]);
end;

function TNumericMarkers.Get(Index: Integer): TNumericMarker;
begin
  result:=TNumericMarker(inherited {$IFDEF D18}Items[Index]{$ELSE}Get(Index){$ENDIF});
end;

procedure TNumericMarkers.Put(Index: Integer; const Value: TNumericMarker);
begin
  Items[Index]:=Value;
end;

procedure TNumericMarkers.SetParentChart(const Value: TCustomTeePanel);
var t : Integer;
begin
  for t:=0 to Count-1 do
      Items[t].Shape.ParentChart:=Value;
end;

type
  TShadowAccess=class(TTeeShadow);
  TGradientAccess=class(TCustomTeeGradient);
  TShapeAccess=class(TTeeShape);

{ TCustomGauge }

Constructor TCustomGauge.Create(AOwner: TComponent);

  procedure InitShadow(const AShadow:TTeeShadow);
  begin
    AShadow.Visible:=False;
    TShadowAccess(AShadow).DefaultVisible:=False;
  end;

begin
  inherited;

  ShowInLegend:=False;
  CalcVisiblePoints:=False;

  FFace:=TTeeShape.Create(nil);

  FFace.Visible:=False; // Changed default 2014.12
  TShapeAccess(FFace).DefaultVisible:=False;

  with TGradientAccess(FFace.Gradient) do
  begin
    Visible:=True;
    IDefVisible:=True;
    InitDirection(gdBottomTop);
  end;

  FFace.Brush.Color:={$IFDEF FMX}claWhite{$ELSE}clWhite{$ENDIF};

  InitShadow(FFace.Shadow);
  InitShadow(FFace.Emboss);

  Add(100*Random);

  FCustomBounds:=TeeZeroRect;
end;

Destructor TCustomGauge.Destroy;
begin
  IAnimation.Free;

  FreeAndNil(FFace);
  FreeAndNil(FFrame);

  inherited;
end;

procedure TCustomGauge.Assign(Source: TPersistent);
begin
  if Source is TCustomGauge then
  with TCustomGauge(Source) do
  begin
    Self.Face:=Face;
    Self.Frame:=FFrame;
    Self.Value:=Value;
  end;

  inherited;

  if Assigned(FFrame) then
     FFrame.SetDefaultCircled(False); // TV52012495
end;

procedure TCustomGauge.SetFrame(const Value: TFramedBorder);
begin
  if Assigned(Value) then Frame.Assign(Value)
                     else FreeAndNil(FFrame);
end;

class function TCustomGauge.CreateAnimation(const AOwner:TComponent;
                                          const StartValue,EndValue:Double;
                                          Duration:Integer):TNumberAnimation;
begin
  result:=TNumberAnimation.Create(AOwner);

  result.Instance:=AOwner;
  result.PropertyName:='Value';
  result.StartValue:=StartValue;
  result.EndValue:=EndValue;
  result.Duration:=Duration;
  result.TwoWay:=True;
  result.KeepEndValue:=True;

  result.Animate:=TTeeAnimate.Create(AOwner);
  result.Animate.Speed:=20;
  result.Animate.Loop:=True;
end;

procedure TCustomGauge.GalleryChanged3D(Is3D: Boolean);
begin
  inherited;
  ParentChart.View3D:=False;
end;

procedure TCustomGauge.PrepareForGallery;
begin
  inherited;

  if not IGalleryStatic then
  begin
    if not Assigned(IAnimation) then
       IAnimation:=CreateAnimation(Self,0,100,150000);

    IAnimation.Animate.Play;
  end;
end;

procedure TCustomGauge.SetGaugePalette(const Palette:Array of TColor);

  {$IFDEF FMX}
  function InvertColor(const AColor:TColor):TColor;
  begin
    with TRGBA(result) do
    begin
      Red:=TRGBA(AColor).Blue;
      Green:=TRGBA(AColor).Green;
      Blue:=TRGBA(AColor).Red;

      // FMX opacity is inverted from VCL:
      if TRGBA(AColor).Alpha=0 then
         Alpha:=255;
    end;
  end;
  {$ENDIF}

var t : Integer;
begin
  GaugeColorPalette:=nil;

  SetLength(GaugeColorPalette,High(Palette)+1);
  for t:=Low(Palette) to High(Palette) do
      GaugeColorPalette[t]:={$IFDEF FMX}InvertColor{$ENDIF}(Palette[t]);

  if Assigned(FFace) then
  begin
    FFace.Gradient.StartColor:=GetPaletteColor(3);
    FFace.Gradient.MidColor:=GetPaletteColor(4);
    FFace.Gradient.EndColor:=GetPaletteColor(5);
    FFace.Gradient.Visible:=True;
  end;

  Repaint;
end;

function TCustomGauge.Clicked(x, y: Integer): Integer;
begin
  if PointInRect(IOrigRect,x,y) then
     result:=0
  else
     result:=TeeNoPointClicked;
end;

Procedure TCustomGauge.SetParentChart(Const Value:TCustomAxisPanel);
begin
  inherited;

  if not (csDestroying in ComponentState) then
  begin
    Face.ParentChart:=Value;

    if Assigned(FFrame) then
       FFrame.ParentChart:=Value;
  end;
end;

procedure TCustomGauge.SetValue(const AValue: TChartValue);
begin
  if Count=0 then
  begin
    Add(AValue);

    if Assigned(FOnChange) then
       FOnChange(Self);
  end
  else
  if MandatoryValueList[0]<>AValue then
  begin
    MandatoryValueList[0]:=AValue;

    Repaint;

    if Assigned(FOnChange) then
       FOnChange(Self);
  end;
end;

function TCustomGauge.GetValue:TChartValue;
begin
  result:=MandatoryValueList[0];
end;

procedure TCustomGauge.SetFace(const Value: TTeeShape);
begin
  Face.Assign(Value);
end;

procedure TCustomGauge.DrawFace;
begin
  if Face.Visible then
     Face.Draw(ParentChart,INewRect);
end;

procedure TCustomGauge.DrawAllValues;
var tmpR : TRect;
begin
  CalcOrigRect;

  tmpR:=IOrigRect;
  INewRect:=IOrigRect;

  SetValues;

  if Assigned(FFrame) and FFrame.Visible then
     FFrame.Draw(tmpR);

  DrawFace;
end;

procedure TCustomGauge.SetValues;
var tmp : Integer;
begin
  if Assigned(FFrame) and FFrame.Visible then
  begin
    tmp:=Round(Frame.CalcWidth(INewRect))-1;
    InflateRect(INewRect,-tmp,-tmp);
  end;
end;

{ TNumericShape }

function TNumericShape.CalcWidth(const Panel:TCustomTeePanel; const AText:String):Integer;
var t,
    tmpCC : Integer;
    c : Char;
  tmpW,
  tmpC : Integer;
  tmpH,
  tmpL : Integer;
begin
  result:=0;

  CalcVariables(Panel.Canvas, tmpW, tmpH, tmpC, tmpL);

  tmpCC:=Round(tmpC/BarFontSpace);

  for t:=1 to Length(AText) do
  begin
    c:=AText[t];

    if (c='.') or (c=',') then
    begin
      //if IsDigital then
      begin
        Inc(result,tmpCC);
        tmpCC:=0;
      end
    end
    else
      Inc(result,tmpW);
  end;
end;

function TNumericShape.CalcTextSize(const Panel:TCustomTeePanel;
              S:String; const TextFormat:TTextFormat; out NumLines:Integer):TPoint;
begin
  result:=inherited CalcTextSize(Panel,S,TextFormat,NumLines);

  if IsElektra or IsDigital then
     result.X:=CalcWidth(Panel,S);
end;

procedure TNumericShape.CalcTextAlign(const Panel: TCustomTeePanel;
                         out X: TCoordinate;
                         const R: TRect; AText:String);
begin
  if IsElektra or IsDigital then
  begin
    case TextAlignment of
      taLeftJustify:
      begin
        Panel.Canvas.TextAlign:=ta_Left;
        x:=R.Left+Margins.Size.Left;

        if Pen.Visible then
           x:=x+Self.Pen.Width;
      end;

      taCenter:
      begin
        Panel.Canvas.TextAlign:=ta_Center;

        with R do
          x:=1+((Left+Margins.Size.Left+
             (Right-Margins.Size.Right-CalcWidth(Panel,AText))) {$IFDEF FMX}*0.5{$ELSE}div 2{$ENDIF});
      end;
    else
    begin
      Panel.Canvas.TextAlign:=ta_Left;
      x:=R.Right-Margins.Size.Right-CalcWidth(Panel,AText);
    end;
    end;
  end
  else
    inherited;
end;

procedure TNumericShape.CalcVariables(const ACanvas:TTeeCanvas; out AWidth, AHeight, AComma, AQuote: Integer);
var tmpSpace : Single;
    tmpChar  : String;
begin
  AHeight:=Round(ACanvas.FontHeight * 0.5);

  if IsDigital then
     tmpSpace:=BarFontSpace
  else
     tmpSpace:=DotFontSpace*0.8;

  // Important:
  // Due to differences in GDI+ vs GDI in TextWidth (see: TGDIPlusCanvas
  // TextWidth method, TGPStringFormat.GenericTypographic parameter),
  // in GDI+ we need to use '1' to obtain a smaller width value than in GDI.

  if ACanvas.SupportsAlpha then
     tmpChar:='1'
  else
     tmpChar:='5';

  AWidth:=Round(ACanvas.TextWidth(tmpChar) * ITextWidth * tmpSpace) + Font.InterCharSize;

  if IsDigital then
     tmpSpace:=BarFontSpace
  else
     tmpSpace:=DotFontSpace;

  AComma:=Round(ACanvas.TextWidth('.') * tmpSpace * 0.5);
  AQuote:=Round(ACanvas.TextWidth('''') * tmpSpace);
end;

procedure TNumericShape.DrawString(const Panel:TCustomTeePanel;
                             const X:TCoordinate;
                             const Y,Z:Integer;
                             const Text:String;
                             AllowHTML:Boolean;
                             AAngle:Integer;
                             const RotationCenter:TPoint);
var tmpY, tmpX,
    t : Integer;
    tmpW, tmpH, tmpC, tmpL: Integer;
    str : String;
    c : Char;
begin
  if IsElektra or IsDigital then
  begin
    CalcVariables(Panel.Canvas, tmpW, tmpH, tmpC, tmpL);

    tmpX:={$IFDEF FMX}Round{$ENDIF}(X);
    tmpY:={$IFDEF FMX}Round{$ENDIF}(Y);
    str:=Text;

    for t := 1 to Length(str) do
    begin
      c:=str[t];

      if c='.' then
         //tmpX:=tmpX-tmpC
      else
      if c=',' then
      begin
        {
         if IsDigital then tmpX:=tmpX-tmpC
         else
         begin
           c:='''';
           tmpX:=tmpX-tmpL;
           tmpY:=Y + tmpH + 1;
         end;
        }
      end;

      if c='''' then
         Panel.Canvas.TextOut(tmpX, tmpY, c)
      else
         Panel.Canvas.TextOut(tmpX, Y, c);

      if c='.' then
         //tmpX:=tmpX+tmpC
         tmpX:=tmpX+tmpC
      else
      if (c=',') and IsDigital then
         tmpX:=tmpX+tmpC
      else
      if (c='''') and IsElektra then
         tmpX:=tmpX+tmpL
      else
         tmpX:=tmpX+tmpW;
    end;
  end
  else
    inherited;
end;

function TNumericShape.IsDigital: Boolean;
begin
  result:=Font.Name=TeeFontDigital;
end;

function TNumericShape.IsElektra: Boolean;
begin
  result:=Font.Name=TeeFontElektra;
end;

initialization
  RegisterTeeSeries(TNumericGauge, @TeeMsg_NumericGauge,
                                  @TeeMsg_GalleryGauges,1);

  {$IFDEF FMX}RegisterFmxClasses{$ELSE}RegisterClasses{$ENDIF}([TNumericMarker]);
finalization
  UnRegisterTeeSeries([TNumericGauge]);
end.
