teegauges.pas

来自「Delphi TeeChartPro.6.01的源代码」· PAS 代码 · 共 542 行

PAS
542
字号
{*********************************************}
{  TeeChart Gauge Series Types                }
{  Copyright (c) 2002-2003 by David Berneda   }
{  All Rights Reserved                        }
{*********************************************}
unit TeeGauges;
{$I TeeDefs.inc}

interface

uses {$IFNDEF LINUX}
     Windows,
     {$ENDIF}
     {$IFDEF CLX}
     Types,
     {$ENDIF}
     Classes, Series, TeEngine;

const TeeHandDistance=30;  // default pixels distance from center to labels

type
  THandStyle=(hsLine,hsTriangle);

  TGaugeSeries=class(TCircledSeries)
  private
    FAngle         : Double;
    FCenter        : TSeriesPointer;
    FDistance      : Integer;
    FEndPoint      : TSeriesPointer;
    FFullRepaint   : Boolean;
    FMax           : Double;
    FMin           : Double;
    FMinorDistance : Integer;
    FStyle         : THandStyle;

    FOnChange: TNotifyEvent;

    ICenter   : TPoint;
    FLabelsInside: Boolean;

    procedure CalcLinePoints(var P0,P1:TPoint);
    Function CalcPoint(const Angle:Double; Center:TPoint;
                       const RadiusX,RadiusY:Double):TPoint;
    procedure DrawValueLine;
    procedure SetAngle(const Value: Double);
    procedure SetCenter(const Value: TSeriesPointer);
    procedure SetDistance(const Value: Integer);
    procedure SetMax(const AValue: Double);
    procedure SetMin(const AValue: Double);
    procedure SetStyle(const Value: THandStyle);
    procedure SetValue(const AValue: Double);
    procedure SetFullRepaint(const Value: Boolean);
    function GetValue: Double;
    procedure SetEndPoint(const Value: TSeriesPointer);
    function SizePointer(APointer:TSeriesPointer):Integer;
    procedure SetMinorDistance(const Value: Integer);
    procedure SetLabelsInside(const Value: Boolean);
  protected
    Procedure AddSampleValues(NumValues:Integer); override;
    procedure DrawAllValues; override;
    Procedure GalleryChanged3D(Is3D:Boolean); override;
    class Function GetEditorClass:String; override;
    Procedure PrepareForGallery(IsEnabled:Boolean); override;
    Procedure SetParentChart(Const Value:TCustomAxisPanel); override;
  public
    Constructor Create(AOwner:TComponent); override;
    Destructor Destroy; override;

    function Axis:TChartAxis;
    Function Clicked(x,y:Integer):Integer; override;
    Function NumSampleValues:Integer; override;
    function MaxYValue:Double; override;
    function MinYValue:Double; override;
  published
    property Center:TSeriesPointer read FCenter write SetCenter;
    property Circled default True;
    property CircleGradient;
    property EndPoint:TSeriesPointer read FEndPoint write SetEndPoint;
    property FullRepaint:Boolean read FFullRepaint write SetFullRepaint default False;
    property Maximum:Double read FMax write SetMax;
    property Minimum:Double read FMin write SetMin;
    property MinorTickDistance:Integer read FMinorDistance write SetMinorDistance default 0;
    property HandDistance:Integer read FDistance write SetDistance default TeeHandDistance;
    property HandStyle:THandStyle read FStyle write SetStyle default hsLine;
    property LabelsInside:Boolean read FLabelsInside write SetLabelsInside default True;
    property RotationAngle default 135;
    property ShowInLegend default False;
    property TotalAngle:Double read FAngle write SetAngle;
    property Value:Double read GetValue write SetValue;
    { events }
    property OnChange:TNotifyEvent read FOnChange write FOnChange;
  end;

implementation

uses {$IFDEF CLX}
     QGraphics,
     {$ELSE}
     Graphics,
     {$ENDIF}
     SysUtils, Math, Chart, TeCanvas, TeeProcs, TeeConst, TeeProCo;

{ TGaugeSeries }

constructor TGaugeSeries.Create(AOwner: TComponent);
begin
  inherited;

  FDistance:=TeeHandDistance;

  Circled:=True;
  ShowInLegend:=False;
  FLabelsInside:=True;

  FCenter:=TSeriesPointer.Create(Self);
  with FCenter do
  begin
    Brush.Style:=bsSolid;
    Color:=clBlack;
    Style:=psCircle;
    HorizSize:=8;
    VertSize:=8;
    Gradient.Visible:=True;
  end;

  FEndPoint:=TSeriesPointer.Create(Self);
  with FEndPoint do
  begin
    Brush.Style:=bsSolid;
    Color:=clWhite;
    Style:=psCircle;
    HorizSize:=3;
    VertSize:=3;
    Visible:=False;
  end;

  Add(0);
  Maximum:=100;
  TotalAngle:=90;
  RotationAngle:=135;
end;

destructor TGaugeSeries.Destroy;
begin
  FEndPoint.Free;
  FCenter.Free;
  inherited;
end;

Function TGaugeSeries.CalcPoint(const Angle:Double; Center:TPoint;
                          const RadiusX,RadiusY:Double):TPoint;
var tmpSin,
    tmpCos : Extended;
begin
  SinCos(Angle,tmpSin,tmpCos);
  result.X:=Center.X-Round(RadiusX*tmpCos);
  result.Y:=Center.Y-Round(RadiusY*tmpSin);
end;

type TPointerAccess=class(TSeriesPointer);

procedure TGaugeSeries.DrawAllValues;

  Procedure DrawAxis;
  var P3,P4 : TPoint;
      tmpR  : TRect;
  begin
    ParentChart.Canvas.AssignVisiblePen(Axis.Axis);

    P3:=CalcPoint(TeePiStep*RotationAngle,ICenter,CircleWidth,CircleHeight);
    P4:=CalcPoint(TeePiStep*(360-TotalAngle+RotationAngle),ICenter,CircleWidth,CircleHeight);

    tmpR:=ParentChart.ChartRect;

    if Circled then
    with ParentChart do
    begin
      if ChartWidth>ChartHeight then
      begin
        tmpR.Left:=ChartXCenter-(ChartHeight div 2);
        tmpR.Right:=ChartXCenter+(ChartHeight div 2);
      end
      else
      begin
        tmpR.Top:=ChartYCenter-(ChartWidth div 2);
        tmpR.Bottom:=ChartYCenter+(ChartWidth div 2);
      end;
    end;

    with tmpR do
         ParentChart.Canvas.Arc(Left+1,Top+1,Right,Bottom,P3.x,P3.y,P4.x,P4.y);
  end;

var tmp : Double;
    tmpS  : String;
    IRange,
    tmpValue,
    tmpStep,
    tmpStep2 : Double;
    tmpXRad,
    tmpYRad : Double;
    tmpFontH,
    t     : Integer;
    P3,
    P4   : TPoint;
begin
  Axis.Increment:=10;
  ICenter.X:=CircleXCenter;
  ICenter.Y:=CircleYCenter;
  IRange:=Maximum-Minimum;
  tmpStep:=Axis.Increment;

  if CircleGradient.Visible then
     DrawCircleGradient;

  with ParentChart,Canvas do
  begin
    // center
    if FCenter.Visible then
    begin
      TPointerAccess(FCenter).PrepareCanvas;
      FCenter.Draw(ICenter);
    end;

    // ticks and labels
    if Axis.Ticks.Visible or Axis.Labels then
    begin
      AssignFont(Axis.Items.Format.Font);

      AssignVisiblePen(Axis.Ticks);
      BackMode:=cbmTransparent;

      tmpXRad:=(XRadius-Axis.TickLength);
      tmpYRad:=(YRadius-Axis.TickLength);

      tmpFontH:=FontHeight;

      if tmpStep<>0 then
      begin
        tmpValue:=Minimum;

        repeat
          tmp:=TotalAngle-(tmpValue*TotalAngle/IRange);
          tmp:=TeePiStep*(360-tmp+RotationAngle);

          P3:=CalcPoint(tmp,ICenter,tmpXRad,tmpYRad);
          P4:=CalcPoint(tmp,ICenter,XRadius,YRadius);

          if Axis.Ticks.Visible then Line(P3,P4);

          if Axis.Labels then
          begin
            tmpS:=FormatFloat(ValueFormat,tmpValue);

            if not LabelsInside then
               P3:=CalcPoint(tmp,ICenter,XRadius+tmpFontH,YRadius+tmpFontH);

            Dec(P3.x,Round(TextWidth(tmpS)*0.5));
            TextOut(P3.x,P3.y,tmpS);
          end;

          tmpValue:=tmpValue+tmpStep;
        Until tmpValue>Maximum;
      end;
    end;

    // minor ticks
    if Axis.MinorTicks.Visible and (Axis.MinorTickCount>0) then
    begin
      AssignVisiblePen(Axis.MinorTicks);

      tmpXRad:=XRadius-Axis.MinorTickLength-MinorTickDistance;
      tmpYRad:=YRadius-Axis.MinorTickLength-MinorTickDistance;

      if tmpStep<>0 then
      begin
        tmpStep2:=tmpStep/(Axis.MinorTickCount+1);

        tmpValue:=Minimum;
        repeat
          for t:=1 to Axis.MinorTickCount do
          begin
            tmp:=TotalAngle-((tmpValue+t*tmpStep2)*TotalAngle/IRange);
            tmp:=TeePiStep*(360-tmp+RotationAngle);

            Line( CalcPoint(tmp,ICenter,tmpXRad,tmpYRad),
                  CalcPoint(tmp,ICenter,XRadius-MinorTickDistance,YRadius-MinorTickDistance)
                );

          end;

          tmpValue:=tmpValue+tmpStep;
        Until tmpValue>(Maximum-tmpStep);
      end;
    end;

    // axis
    if Axis.Axis.Visible then DrawAxis;

    // value line
    if Self.Pen.Visible then DrawValueLine;
  end;
end;

Function TGaugeSeries.Clicked(x,y:Integer):Integer;
var P0,P1 : TPoint;
begin
  CalcLinePoints(P0,P1);
  if Assigned(ParentChart) then
     ParentChart.Canvas.Calculate2DPosition(X,Y,MiddleZ);

  if PointInLine(TeePoint(x,y),P0,P1,2) then
     result:=0
  else
     result:=TeeNoPointClicked;
end;

function TGaugeSeries.SizePointer(APointer:TSeriesPointer):Integer;
begin
  if APointer.Visible then
  begin
    result:=2*APointer.VertSize;
    if APointer.Pen.Visible then Inc(result,APointer.Pen.Width);
  end
  else result:=0;
end;

procedure TGaugeSeries.CalcLinePoints(var P0,P1:TPoint);
var tmp : Double;
    tmpDist : Integer;
begin
  tmp:=TeePiStep*(360-(TotalAngle-((Value-Minimum)*TotalAngle/(Maximum-Minimum)))+RotationAngle);

  P1:=CalcPoint(tmp,ICenter,XRadius,YRadius);

  tmpDist:=HandDistance+SizePointer(FEndPoint);
  if tmpDist>0 then
     P1:=PointAtDistance(ICenter,P1,tmpDist);

  if FCenter.Visible and (FCenter.Style=psCircle) then
     P0:=PointAtDistance(P1,ICenter,SizePointer(FCenter) div 2)
  else
     P0:=ICenter;
end;

procedure TGaugeSeries.DrawValueLine;
var tmp : Double;
    P4  : TPoint;
    tmpCenter : TPoint;
begin
  CalcLinePoints(tmpCenter,P4);

  if not FullRepaint then Pen.Mode:=pmNotXor
                     else Pen.Mode:=pmCopy;

  ParentChart.Canvas.AssignVisiblePen(Pen);

  if HandStyle=hsLine then
     ParentChart.Canvas.Line(tmpCenter,P4)
  else
  begin
    tmp:=ArcTan2(P4.y-tmpCenter.X,P4.x-tmpCenter.Y);
    ParentChart.Canvas.Polygon([CalcPoint(tmp,tmpCenter,4,4),P4,CalcPoint(tmp+Pi,tmpCenter,4,4)]);
  end;

  // end point
  if FEndPoint.Visible then
  begin
    if not FullRepaint then FEndPoint.Pen.Mode:=pmNotXor
                       else FEndPoint.Pen.Mode:=pmCopy;

    TPointerAccess(FEndPoint).PrepareCanvas;

    if not FullRepaint then ParentChart.Canvas.Brush.Style:=bsClear;

    P4:=PointAtDistance(ICenter,P4,-SizePointer(FEndPoint) div 2);
    FEndPoint.Draw(P4);
  end;
end;

procedure TGaugeSeries.SetAngle(const Value: Double);
begin
  SetDoubleProperty(FAngle,Value);
end;

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

procedure TGaugeSeries.SetDistance(const Value: Integer);
begin
  SetIntegerProperty(FDistance,Value);
end;

procedure TGaugeSeries.SetMax(const AValue: Double);
begin
  SetDoubleProperty(FMax,AValue);
  Value:=Math.Min(Maximum,Value);
end;

procedure TGaugeSeries.SetMin(const AValue: Double);
begin
  SetDoubleProperty(FMin,AValue);
  Value:=Math.Max(Minimum,Value);
end;

procedure TGaugeSeries.SetParentChart(const Value: TCustomAxisPanel);
begin
  inherited;
  if not (csDestroying in ComponentState) then
  begin
    FCenter.ParentChart:=ParentChart;
    FEndPoint.ParentChart:=ParentChart;
  end;

  if Assigned(ParentChart) then
  begin
    with TCustomChart(ParentChart) do
    begin
      Walls.Visible:=False;
      View3D:=False;
      Frame.Visible:=False;
    end;

    Axis.TickLength:=14;
    Axis.MinorTickLength:=6;
  end;
end;

procedure TGaugeSeries.SetStyle(const Value: THandStyle);
begin
  if FStyle<>Value then
  begin
    FStyle:=Value;
    Repaint;
  end;
end;

procedure TGaugeSeries.SetValue(const AValue: Double);
begin
  if Value<>AValue then
  begin
    if not FullRepaint then DrawValueLine;
    MandatoryValueList.Value[0]:=AValue;
    if FullRepaint then Repaint
                   else DrawValueLine;

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

procedure TGaugeSeries.SetFullRepaint(const Value: Boolean);
begin
  SetBooleanProperty(FFullRepaint,Value);
end;

function TGaugeSeries.GetValue: Double;
begin
  if Count=0 then Add(0);
  result:=MandatoryValueList.Value[0];
end;

procedure TGaugeSeries.SetEndPoint(const Value: TSeriesPointer);
begin
  FEndPoint.Assign(Value);
end;

procedure TGaugeSeries.AddSampleValues(NumValues: Integer);
begin
  Value:=Minimum+(Maximum-Minimum)*Random;
end;

procedure TGaugeSeries.PrepareForGallery(IsEnabled: Boolean);
begin
  inherited;

  with Axis do
  begin
    MinorTickCount:=1;
    TickLength:=4;
    MinorTickLength:=2;
    Increment:=25;
  end;

  HandDistance:=3;
  Center.VertSize:=2;
  Center.HorizSize:=2;
  FullRepaint:=True;

  if IsEnabled then Pen.Color:=clBlue
               else Pen.Color:=clDkGray;
end;

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

procedure TGaugeSeries.SetMinorDistance(const Value: Integer);
begin
  SetIntegerProperty(FMinorDistance,Value);
end;

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

class function TGaugeSeries.GetEditorClass: String;
begin
  result:='TGaugeSeriesEditor';
end;

function TGaugeSeries.MaxYValue: Double;
begin
  result:=FMax;
end;

function TGaugeSeries.MinYValue: Double;
begin
  result:=FMin;
end;

function TGaugeSeries.Axis: TChartAxis;
begin
  result:=GetVertAxis;
end;

procedure TGaugeSeries.SetLabelsInside(const Value: Boolean);
begin
  SetBooleanProperty(FLabelsInside,Value);
end;

initialization
  RegisterTeeSeries(TGaugeSeries,@TeeMsg_GalleryGauge,@TeeMsg_GalleryExtended,1);
finalization
  UnRegisterTeeSeries([TGaugeSeries]);
end.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?