lazarus/components/tachart/taseries.pas
ask 4bd8cdc586 TAChart: implement all MarksStyle values for TPieSeries.
Add mark style selection to the demo application.

git-svn-id: trunk@18794 -
2009-02-22 15:10:53 +00:00

1664 lines
44 KiB
ObjectPascal

{
/***************************************************************************
TASeries.pas
------------
Component Library Standard Graph Series
***************************************************************************/
*****************************************************************************
* *
* See the file COPYING.modifiedLGPL.txt, included in this distribution, *
* for details about the copyright. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
*****************************************************************************
Authors: Luís Rodrigues, Philippe Martinole, Alexander Klenin
}
unit TASeries;
{$H+}
interface
uses
Classes, Dialogs, Graphics, sysutils, TAGraph, TAChartUtils;
type
TChartCoord = record
x, y: Double;
Color: TColor;
Text: String;
end;
PChartCoord = ^TChartCoord;
TSeriesPointerStyle = (
psRectangle, psCircle, psCross, psDiagCross, psStar,
psLowBracket, psHighBracket);
BarException = class(Exception);
{ TChartSeries }
TChartSeries = class(TBasicChartSeries)
private
// Graph = coordinates in the graph
FXGraphMin, FYGraphMin: Double; // Max Graph value of points
FXGraphMax, FYGraphMax: Double;
FCoordList: TList;
FActive: Boolean;
FMarks: TSeriesMarksStyle;
FShowInLegend: Boolean;
FValuesTotal: Double;
FValuesTotalValid: Boolean;
procedure SetActive(Value: Boolean);
procedure SetMarks(Value: TSeriesMarksStyle);
function GetXMinVal: Integer;
procedure SetShowInLegend(Value: Boolean);
procedure InitBounds(out XMin, YMin, XMax, YMax: Integer);
protected
procedure StyleChanged(Sender: TObject);
property Coord: TList read FCoordList;
procedure DrawLegend(ACanvas: TCanvas; const ARect: TRect); override;
function GetLegendWidth(ACanvas: TCanvas): Integer; override;
function GetLegendCount: Integer; override;
function IsInLegend: Boolean; override;
procedure UpdateBounds(
var ANumPoints: Integer; var AXMin, AYMin, AXMax, AYMax: Double); override;
procedure UpdateParentChart;
function GetValuesTotal: Double;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property XGraphMin: Double read FXGraphMin write FXGraphMin;
property YGraphMin: Double read FYGraphMin write FYGraphMin;
property XGraphMax: Double read FXGraphMax write FXGraphMax;
property YGraphMax: Double read FYGraphMax write FYGraphMax;
function Count: Integer; override;
procedure Draw(ACanvas: TCanvas); virtual; abstract;
procedure DrawIfActive(ACanvas: TCanvas); override;
function AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint; virtual;
function Add(AValue: Double; XLabel: String; Color: TColor): Longint; virtual;
procedure Delete(AIndex: Integer); virtual;
procedure Clear;
function FormattedMark(AIndex: integer): String;
published
property Active: Boolean read FActive write SetActive default true;
property MarksStyle: TSeriesMarksStyle
read FMarks write SetMarks default smsLabel; //this should be an object
property ShowInLegend: Boolean
read FShowInLegend write SetShowInLegend default true;
property Title;
end;
TSeriesPointer = class(TPersistent)
private
FHorizSize, FVertSize: Integer;
FStyle: TSeriesPointerStyle;
FPen: TChartPen;
FBrush: TBrush;
FVisible: Boolean;
FOwner: TChartSeries;
FChanged: TNotifyEvent;
procedure SetVisible(Value: Boolean);
procedure SetStyle(Value: TSeriesPointerStyle);
procedure SetPen(Value: TChartPen);
procedure SetBrush(Value: TBrush);
procedure SetHorizSize(Value: Integer);
procedure SetVertSize(Value: Integer);
protected
public
constructor Create(AOwner: TChartSeries);
destructor Destroy; override;
procedure Draw(ACanvas: TCanvas; px, py: Integer; AColor: TColor = clTAColor);
property ParentSeries: TChartSeries read FOwner;
procedure Assign(Source: TPersistent); override;
published
property Brush: TBrush read FBrush write SetBrush;
property HorizSize: Integer read FHorizSize write SetHorizSize default 4;
property OnChange: TNotifyEvent read FChanged write FChanged;
property Pen: TChartPen read FPen write SetPen;
property Style: TSeriesPointerStyle read FStyle write SetStyle default psRectangle;
property VertSize: Integer read FVertSize write SetVertSize default 4;
property Visible: Boolean read FVisible write SetVisible;
end;
{ TBarSeries }
TBarSeries = class(TChartSeries)
private
FBarBrush: TBrush;
FBarPen: TPen;
FBarWidthPercent: Integer;
procedure SetBarWidthPercent(Value: Integer);
procedure SetBarBrush(Value: TBrush);
procedure SetBarPen(Value: TPen);
procedure ExamineAllBarSeries(out ATotalNumber, AMyPos: Integer);
protected
procedure DrawLegend(ACanvas: TCanvas; const ARect: TRect); override;
function GetSeriesColor: TColor; override;
procedure SetSeriesColor(const AValue: TColor); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Draw(ACanvas: TCanvas); override;
function AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint; override;
published
property BarBrush: TBrush read FBarBrush write SetBarBrush;
property BarPen: TPen read FBarPen write SetBarPen;
property BarWidthPercent: Integer
read FBarWidthPercent write SetBarWidthPercent default 70;
property SeriesColor;
end;
{ TPieSeries }
TPieSeries = class(TChartSeries)
private
ColorIndex: Integer;
FMiscColors: array [1..3] of TColor;
function GetMiscColor(AIndex: integer): TColor;
procedure SetMiscColor(AIndex: integer; const AValue: TColor);
protected
procedure DrawLegend(ACanvas: TCanvas; const ARect: TRect); override;
function GetLegendCount: Integer; override;
function GetLegendWidth(ACanvas: TCanvas): Integer; override;
procedure AfterAdd; override;
function GetSeriesColor: TColor; override;
procedure SetSeriesColor(const AValue: TColor); override;
public
constructor Create(AOwner: TComponent); override;
procedure Draw(ACanvas: TCanvas); override;
function AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint; override;
function AddPie(Value: Double; Text: String; Color: TColor): Longint;
published
property LabelTextColor: TColor index 1
read GetMiscColor write SetMiscColor default clBlack;
property LabelBackgroundColor: TColor index 2
read GetMiscColor write SetMiscColor default clYellow;
property LabelToPieLinkColor: TColor index 3
read GetMiscColor write SetMiscColor default clWhite;
end;
{ TAreaSeries }
TAreaSeries = class(TChartSeries)
private
FAreaLinesPen: TChartPen;
FAreaBrush: TBrush;
FStairs: Boolean;
FInvertedStairs: Boolean;
procedure SetAreaBrush(Value: TBrush);
procedure SetStairs(Value: Boolean);
procedure SetInvertedStairs(Value: Boolean);
protected
procedure DrawLegend(ACanvas: TCanvas; const ARect: TRect); override;
function GetSeriesColor: TColor; override;
procedure SetSeriesColor(const AValue: TColor); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Draw(ACanvas: TCanvas); override;
function AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint; override;
published
property AreaLinesPen: TChartPen read FAreaLinesPen write FAreaLinesPen;
property AreaBrush: TBrush read FAreaBrush write SetAreaBrush;
property InvertedStairs: Boolean read FInvertedStairs write SetInvertedStairs;
property SeriesColor;
property Stairs: Boolean read FStairs write SetStairs;
end;
{ TBasicLineSeries }
TBasicLineSeries = class(TChartSeries)
protected
procedure DrawLegend(ACanvas: TCanvas; const ARect: TRect); override;
end;
{ TSerie }
TSerie = class(TBasicLineSeries)
private
FPointer: TSeriesPointer;
FStyle: TPenStyle;
FSeriesColor: TColor;
XOfYGraphMin, XOfYGraphMax: Double; // X max value of points
FShowPoints: Boolean;
FShowLines: Boolean;
UpdateInProgress: Boolean;
procedure SetShowPoints(Value: Boolean);
procedure SetShowLines(Value: Boolean);
procedure SetPointer(Value: TSeriesPointer);
protected
function GetNearestPoint(
ADistFunc: TPointDistFunc; const APoint: TPoint;
out AIndex: Integer; out AImg: TPoint; out AValue: TDoublePoint): Boolean;
override;
function GetSeriesColor: TColor; override;
procedure SetSeriesColor(const AValue: TColor); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Draw(ACanvas: TCanvas); override;
function AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint; override;
function GetXValue(AIndex: Integer): Double;
function GetYValue(AIndex: Integer): Double;
procedure SetXValue(AIndex: Integer; Value: Double);
procedure SetYValue(AIndex: Integer; Value: Double);
function GetXImgValue(AIndex: Integer): Integer;
function GetYImgValue(AIndex: Integer): Integer;
procedure GetMin(var X, Y: Double);
procedure GetMax(var X, Y: Double);
function GetXMin: Double;
function GetXMax: Double;
function GetYMin: Double;
function GetYMax: Double;
procedure SetColor(AIndex: Integer; AColor: TColor);
function GetColor(AIndex: Integer): TColor;
procedure BeginUpdate;
procedure EndUpdate;
property XGraphMin;
property YGraphMin;
property XGraphMax;
property YGraphMax;
published
property Pointer: TSeriesPointer read FPointer write SetPointer;
property SeriesColor;
property ShowLines: Boolean read FShowLines write SetShowLines default true;
property ShowPoints: Boolean read FShowPoints write SetShowPoints;
end;
TLineStyle = (lsVertical, lsHorizontal);
{ TLine }
TLine = class(TBasicLineSeries)
private
FPen: TPen;
FPosGraph: Double; // Graph coordinates of line
FStyle: TLineStyle;
procedure SetPen(AValue: TPen);
procedure SetPos(AValue: Double);
procedure SetStyle(AValue: TLineStyle);
procedure Changed;
protected
function GetSeriesColor: TColor; override;
procedure SetSeriesColor(const AValue: TColor); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Draw(ACanvas: TCanvas); override;
published
property LineStyle: TLineStyle read FStyle write SetStyle default lsHorizontal;
property Pen: TPen read FPen write SetPen;
property Position: Double read FPosGraph write SetPos;
property SeriesColor;
end;
implementation
uses
GraphMath, Math, Types;
constructor TChartSeries.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
XGraphMin := MaxDouble;
YGraphMin := MaxDouble;
XGraphMax := MinDouble;
YGraphMax := MinDouble;
FActive := true;
FShowInLegend := true;
FMarks := smsLabel;
FCoordList := TList.Create;
end;
destructor TChartSeries.Destroy;
var
i: Integer;
begin
for i := 0 to FCoordList.Count - 1 do
Dispose(PChartCoord(FCoordList.Items[i]));
FCoordList.Free;
UpdateParentChart;
inherited Destroy;
end;
procedure TChartSeries.DrawIfActive(ACanvas: TCanvas);
begin
if Active then
Draw(ACanvas);
end;
procedure TChartSeries.DrawLegend(ACanvas: TCanvas; const ARect: TRect);
begin
ACanvas.TextOut(ARect.Right + 3, ARect.Top, Title);
end;
function TChartSeries.FormattedMark(AIndex: integer): String;
begin
with PChartCoord(FCoordList[AIndex])^ do
Result := Format(
SERIES_MARK_FORMATS[MarksStyle],
[y, y / GetValuesTotal * 100, Text, GetValuesTotal, x]);
end;
function TChartSeries.GetLegendCount: Integer;
begin
Result := 1;
end;
function TChartSeries.GetLegendWidth(ACanvas: TCanvas): Integer;
begin
Result := ACanvas.TextWidth(Title);
end;
function TChartSeries.GetValuesTotal: Double;
var
i: Integer;
begin
if not FValuesTotalValid then begin
FValuesTotal := 0;
for i := 0 to FCoordList.Count - 1 do
FValuesTotal += PChartCoord(FCoordList[i])^.y;
FValuesTotalValid := true;
end;
Result := FValuesTotal;
end;
function TChartSeries.GetXMinVal: Integer;
begin
if Count > 0 then
Result := Round(PChartCoord(FCoordList[FCoordList.Count-1])^.x)
else
Result := 0;
end;
procedure TChartSeries.InitBounds(out XMin, YMin, XMax, YMax: Integer);
begin
with ParentChart do begin
XMin := XImageMin;
XMax := XImageMax;
YMin := YImageMin;
YMax := YImageMax;
end;
if XMin > XMax then
Exchange(XMin, XMax);
if YMin > YMax then
Exchange(YMin, YMax);
end;
function TChartSeries.IsInLegend: Boolean;
begin
Result := Active and ShowInLegend;
end;
function TChartSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint;
var
pcc: PChartCoord;
begin
New(pcc);
pcc^.x := X;
pcc^.y := Y;
pcc^.Color := Color;
pcc^.Text := XLabel;
// We keep FCoordList ordered by X coordinate.
// Note that this leads to O(N^2) time except
// for the case of adding already ordered points.
// So, is the user wants to add many (>10000) points to a graph,
// he should pre-sort them to avoid performance penalty.
Result := FCoordList.Count;
while (Result > 0) and (PChartCoord(FCoordList.Items[Result - 1])^.x > X) do
Dec(Result);
FCoordList.Insert(Result, pcc);
if FValuesTotalValid then
FValuesTotal += Y;
end;
function TChartSeries.Add(AValue: Double; XLabel: String; Color: TColor): Longint;
var
XVal: Integer;
begin
if FCoordList.Count = 0 then
XVal := 0
else
XVal := Round(PChartCoord(FCoordList.Items[FCoordList.Count - 1])^.x);
Result := AddXY(XVal + 1, AValue, XLabel, Color);
end;
procedure TChartSeries.Delete(AIndex:Integer);
begin
Dispose(PChartCoord(FCoordList.Items[AIndex]));
FCoordList.Delete(AIndex);
FValuesTotalValid := false;
UpdateParentChart;
end;
procedure TChartSeries.Clear;
begin
FCoordList.Clear;
XGraphMin := MaxDouble;
YGraphMin := MaxDouble;
XGraphMax := MinDouble;
YGraphMax := MinDouble;
FValuesTotalValid := false;
UpdateParentChart;
end;
function TChartSeries.Count:Integer;
begin
Result := FCoordList.Count;
end;
procedure TChartSeries.SetActive(Value: Boolean);
begin
FActive := Value;
UpdateParentChart;
end;
procedure TChartSeries.SetShowInLegend(Value: Boolean);
begin
FShowInLegend := Value;
UpdateParentChart;
end;
procedure TChartSeries.StyleChanged(Sender: TObject);
begin
UpdateParentChart;
end;
procedure TChartSeries.UpdateBounds(
var ANumPoints: Integer; var AXMin, AYMin, AXMax, AYMax: Double);
begin
if not Active or (Count = 0) then exit;
ANumPoints += Count;
if XGraphMin < AXMin then AXMin := XGraphMin;
if YGraphMin < AYMin then AYMin := YGraphMin;
if XGraphMax > AXMax then AXMax := XGraphMax;
if YGraphMax > AYMax then AYMax := YGraphMax;
end;
procedure TChartSeries.UpdateParentChart;
begin
if ParentChart <> nil then ParentChart.Invalidate;
end;
procedure TChartSeries.SetMarks(Value: TSeriesMarksStyle);
begin
FMarks := Value;
UpdateParentChart;
end;
{ TSeriesPointer }
procedure TSeriesPointer.SetVisible(Value: Boolean);
begin
FVisible := Value;
if Assigned(FChanged) then FChanged(Self);
end;
procedure TSeriesPointer.SetStyle(Value: TSeriesPointerStyle);
begin
FStyle := Value;
if Assigned(FChanged) then FChanged(Self);
end;
procedure TSeriesPointer.SetPen(Value: TChartPen);
begin
FPen.Assign(Value);
if Assigned(FChanged) then FChanged(Self);
end;
procedure TSeriesPointer.SetBrush(Value: TBrush);
begin
FBrush.Assign(Value);
if Assigned(FChanged) then FChanged(Self);
end;
procedure TSeriesPointer.SetHorizSize(Value: Integer);
begin
FHorizSize := Value;
if Assigned(FChanged) then FChanged(Self);
end;
procedure TSeriesPointer.SetVertSize(Value: Integer);
begin
FVertSize := Value;
if Assigned(FChanged) then FChanged(Self);
end;
constructor TSeriesPointer.Create(AOwner: TChartSeries);
begin
FBrush := TBrush.Create;
FBrush.Color := clLime;
FPen := TChartPen.Create;
FOwner := AOwner;
FHorizSize := 4;
FVertSize := 4;
end;
destructor TSeriesPointer.Destroy;
begin
FBrush.Free;
FPen.Free;
inherited Destroy;
end;
procedure TSeriesPointer.Draw(ACanvas: TCanvas; px, py: Integer; AColor: TColor);
begin
with FOwner do begin
if AColor = clTAColor then
AColor := SeriesColor;
ACanvas.Brush.Assign(FBrush);
ACanvas.Pen.Assign(FPen);
case FStyle of
psRectangle: begin
ACanvas.Brush.Color := AColor;
ACanvas.Rectangle(px-FHorizSize,py-FVertSize,px+FHorizSize+1,py+FVertSize+1);
end;
psCross: begin
ACanvas.Pen.Color := AColor;
ACanvas.MoveTo(px-FHorizSize,py);
ACanvas.LineTo(px+FHorizSize+1,py);
ACanvas.MoveTo(px,py-FVertSize);
ACanvas.LineTo(px,py+FVertSize+1);
end;
psDiagCross: begin
ACanvas.Pen.Color := AColor;
ACanvas.MoveTo(px-FHorizSize,py-FVertSize);
ACanvas.LineTo(px+FHorizSize+1,py+FVertSize+1);
ACanvas.MoveTo(px-FHorizSize,py+FVertSize+1);
ACanvas.LineTo(px+FHorizSize+1,py-FVertSize);
end;
psStar: begin
ACanvas.Pen.Color := AColor;
ACanvas.MoveTo(px-FHorizSize,py);
ACanvas.LineTo(px+FHorizSize+1,py);
ACanvas.MoveTo(px,py-FVertSize);
ACanvas.LineTo(px,py+FVertSize+1);
ACanvas.MoveTo(px-FHorizSize,py-FVertSize);
ACanvas.LineTo(px+FHorizSize+1,py+FVertSize+1);
ACanvas.MoveTo(px-FHorizSize,py+FVertSize+1);
ACanvas.LineTo(px+FHorizSize+1,py-FVertSize);
end;
psCircle: begin
ACanvas.Brush.Color := AColor;
ACanvas.Ellipse(px-FHorizSize,py-FVertSize,px+FHorizSize+1,py+FVertSize+1);
end;
psLowBracket: begin
ACanvas.Pen.Color := AColor;
ACanvas.MoveTo(px-FHorizSize,py);
ACanvas.LineTo(px-FHorizSize,py+FVertSize+1);
ACanvas.LineTo(px+FHorizSize+1,py+FVertSize+1);
ACanvas.LineTo(px+FHorizSize+1,py-1);
end;
psHighBracket: begin
ACanvas.Pen.Color := AColor;
ACanvas.MoveTo(px-FHorizSize,py);
ACanvas.LineTo(px-FHorizSize,py-FVertSize);
ACanvas.LineTo(px+FHorizSize+1,py-FVertSize);
ACanvas.LineTo(px+FHorizSize+1,py+1);
end;
end;
end;
end;
procedure TSeriesPointer.Assign(Source: TPersistent);
begin
if Source is TSeriesPointer then
with TSeriesPointer(Source) do begin
FBrush.Assign(Brush);
FPen.Assign(Pen);
FStyle := Style;
FVisible := Visible;
end;
inherited Assign(Source);
end;
{ TSerie }
constructor TSerie.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPointer := TSeriesPointer.Create(Self);
FPointer.FStyle := psCross;
FPointer.OnChange := @StyleChanged;
FStyle := psSolid;
ShowPoints := false;
ShowLines := true;
UpdateInProgress := false;
end;
destructor TSerie.Destroy;
begin
FPointer.Free;
inherited Destroy;
end;
procedure TSerie.SetPointer(Value: TSeriesPointer);
begin
FPointer.Assign(Value);
UpdateParentChart;
end;
procedure TSerie.SetSeriesColor(const AValue: TColor);
begin
FSeriesColor := AValue;
end;
procedure TSerie.Draw(ACanvas: TCanvas);
var
xi1, yi1, xi2, yi2: Integer;
xg1, yg1, xg2, yg2: Double;
XMin, XMax, YMin, YMax: Integer;
chartCoord: PChartCoord;
function PrepareLine: Boolean;
begin
Result := false;
if not FShowLines then exit;
with ParentChart do
if // line is totally outside the viewport
(xg1 < XGraphMin) and (xg2 < XGraphMin) or
(xg1 > XGraphMax) and (xg2 > XGraphMax) or
(yg1 < YGraphMin) and (yg2 < YGraphMin) or
(yg1 > YGraphMax) and (yg2 > YGraphMax)
then
exit;
Result := true;
with ParentChart do
if // line is totally inside the viewport
InRange(xg1, XGraphMin, XGraphMax) and
InRange(xg2, XGraphMin, XGraphMax) and
InRange(yg1, YGraphMin, YGraphMax) and
InRange(yg2, YGraphMin, YGraphMax)
then
exit;
if yg1 > yg2 then begin
Exchange(xg1, xg2);
Exchange(yg1, yg2);
end;
if yg1 = yg2 then begin
if xg1 > xg2 then begin
Exchange(xg1, xg2);
Exchange(yg1, yg2);
end;
if xg1 < ParentChart.XGraphMin then xi1 := ParentChart.XImageMin;
if xg2 > ParentChart.XGraphMax then xi2 := ParentChart.XImageMax;
end
else if xg1 = xg2 then begin
if yg1 < ParentChart.YGraphMin then yi1 := ParentChart.YImageMin;
if yg2 > ParentChart.YGraphMax then yi2 := ParentChart.YImageMax;
end
else if ParentChart.LineInViewPort(xg1, yg1, xg2, yg2) then begin
ParentChart.GraphToImage(xg1, yg1, xi1, yi1);
ParentChart.GraphToImage(xg2, yg2, xi2, yi2);
end
else
Result := false;
end;
procedure DrawPoint;
begin
if
FShowPoints and InRange(yi1, YMin, YMax) and InRange(xi1, XMin, XMax)
then
FPointer.Draw(ACanvas, xi1, yi1, SeriesColor);
end;
procedure GetCoords(
AIndex: Integer; out AX, AY: Double; out AXScr, AYScr: Integer);
begin
chartCoord := FCoordList.Items[AIndex];
AX := chartCoord^.x;
AY := chartCoord^.y;
ParentChart.GraphToImage(AX, AY, AXScr, AYScr);
end;
var
i: Integer;
begin
if Count = 0 then exit;
InitBounds(XMin, YMin, XMax, YMax);
ACanvas.Pen.Mode := pmCopy;
ACanvas.Pen.Width := 1;
for i := 0 to Count - 2 do begin
GetCoords(i, xg1, yg1, xi1, yi1);
GetCoords(i + 1, xg2, yg2, xi2, yi2);
if PrepareLine then begin
ACanvas.Pen.Style := FStyle;
ACanvas.Pen.Color := chartCoord^.Color;
ACanvas.MoveTo(xi1, yi1);
ACanvas.LineTo(xi2, yi2);
end;
DrawPoint;
end;
// Draw last point
GetCoords(Count - 1, xg1, yg1, xi1, yi1);
DrawPoint;
end;
function TSerie.AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint;
begin
if Color = clTAColor then
Color := SeriesColor;
Result := inherited AddXY(X, Y, XLabel, Color);
// Update max
if X > XGraphMax then XGraphMax := X;
if X < XGraphMin then XGraphMin := X;
if Y > YGraphMax then begin
YGraphMax := Y;
XOfYGraphMax := X;
end;
if Y < YGraphMin then begin
YGraphMin := Y;
XOfYGraphMin := X;
end;
UpdateParentChart;
end;
function TSerie.GetXValue(AIndex: Integer): Double;
begin
Result := PChartCoord(FCoordList.Items[AIndex])^.x;
end;
function TSerie.GetYValue(AIndex: Integer): Double;
begin
Result := PChartCoord(FCoordList.Items[AIndex])^.y;
end;
procedure TSerie.SetXValue(AIndex: Integer; Value: Double);
var
i: Integer;
Val: Double;
begin
if not UpdateInProgress then begin
if Value < XGraphMin then XGraphMin := Value
else if Value > XGraphMax then XGraphMax := Value
else begin
if PChartCoord(FCoordList.Items[AIndex])^.x = XGraphMax then begin
PChartCoord(FCoordList.Items[AIndex])^.x := Value;
if Value < XGraphMax then begin
XGraphMax := MinDouble;
for i := 0 to FCoordList.Count - 1 do begin
Val := PChartCoord(FCoordList.Items[AIndex])^.x;
if Val > XGraphMax then XGraphMax := Val;
end;
end;
end
else if PChartCoord(FCoordList.Items[AIndex])^.x = XGraphMin then begin
PChartCoord(FCoordList.Items[AIndex])^.x := Value;
if Value > XGraphMin then begin
XGraphMin := MaxDouble;
for i := 0 to FCoordList.Count - 1 do begin
Val := PChartCoord(FCoordList.Items[AIndex])^.x;
if Val < XGraphMin then XGraphMin := Val;
end;
end;
end;
end;
end;
PChartCoord(FCoordList.Items[AIndex])^.x := Value;
UpdateParentChart;
end;
procedure TSerie.SetYValue(AIndex: Integer; Value: Double);
var
i: Integer;
Val: Double;
begin
if not UpdateInProgress then begin
if Value<YGraphMin then YGraphMin:=Value
else if Value>YGraphMax then YGraphMax:=Value
else begin
if PChartCoord(FCoordList.Items[AIndex])^.y=YGraphMax then begin
PChartCoord(FCoordList.Items[AIndex])^.y:=Value;
if Value<YGraphMax then begin
YGraphMax:=MinDouble;
for i:=0 to FCoordList.Count-1 do begin
Val:=PChartCoord(FCoordList.Items[AIndex])^.y;
if Val>YGraphMax then YGraphMax:=Val;
end;
end;
end
else if PChartCoord(FCoordList.Items[AIndex])^.y=YGraphMin then begin
PChartCoord(FCoordList.Items[AIndex])^.y:=Value;
if Value>YGraphMin then begin
YGraphMin:=MaxDouble;
for i:=0 to FCoordList.Count-1 do begin
Val:=PChartCoord(FCoordList.Items[AIndex])^.y;
if Val<YGraphMin then YGraphMin:=Val;
end;
end;
end;
end;
end;
PChartCoord(FCoordList.Items[AIndex])^.y := Value;
UpdateParentChart;
end;
function TSerie.GetXImgValue(AIndex: Integer): Integer;
begin
ParentChart.XGraphToImage(PChartCoord(FCoordList.Items[AIndex])^.x, Result);
end;
function TSerie.GetYImgValue(AIndex: Integer): Integer;
begin
ParentChart.YGraphToImage(PChartCoord(FCoordList.Items[AIndex])^.y, Result);
end;
function TSerie.GetXMin: Double;
begin
Result := XGraphMin;
end;
function TSerie.GetXMax: Double;
begin
Result := XGraphMax;
end;
function TSerie.GetYMin: Double;
begin
Result := YGraphMin;
end;
function TSerie.GetYMax: Double;
begin
Result := YGraphMax;
end;
procedure TSerie.GetMax(var X, Y: Double);
begin
X := XOfYGraphMax;
Y := YGraphMax;
end;
procedure TSerie.GetMin(var X, Y: Double);
begin
X := XOfYGraphMin;
Y := YGraphMin;
end;
function TSerie.GetNearestPoint(
ADistFunc: TPointDistFunc; const APoint: TPoint;
out AIndex: Integer; out AImg: TPoint; out AValue: TDoublePoint): Boolean;
var
dist, minDist, i: Integer;
pt: TPoint;
begin
Result := Count > 0;
minDist := MaxInt;
for i := 0 to Count - 1 do begin
pt := Point(GetXImgValue(i), GetYImgValue(i));
dist := ADistFunc(APoint, pt);
if dist >= minDist then
Continue;
minDist := dist;
AIndex := i;
AImg := pt;
AValue.X := GetXValue(i);
AValue.Y := GetYValue(i);
end;
end;
function TSerie.GetSeriesColor: TColor;
begin
Result := FSeriesColor;
end;
procedure TSerie.SetColor(AIndex: Integer; AColor: TColor);
begin
PChartCoord(FCoordList.items[AIndex])^.Color := AColor;
end;
function TSerie.GetColor(AIndex: Integer): TColor;
begin
Result := PChartCoord(FCoordList.items[AIndex])^.Color;
end;
procedure TSerie.SetShowPoints(Value: Boolean);
begin
FShowPoints := Value;
UpdateParentChart;
end;
procedure TSerie.SetShowLines(Value: Boolean);
begin
FShowLines := Value;
UpdateParentChart;
end;
procedure TSerie.BeginUpdate;
begin
UpdateInProgress := true;
end;
procedure TSerie.EndUpdate;
var
i: Integer;
Val: Double;
begin
UpdateInProgress := false;
XGraphMax := MinDouble;
XGraphMin := MaxDouble;
for i := 0 to Count - 1 do begin
Val := PChartCoord(FCoordList.Items[i])^.x;
if Val > XGraphMax then XGraphMax := Val;
if Val < XGraphMin then XGraphMin := Val;
end;
YGraphMax := MinDouble;
YGraphMin := MaxDouble;
for i:=0 to Count-1 do begin
Val := PChartCoord(FCoordList.Items[i])^.y;
if Val > YGraphMax then YGraphMax := Val;
if Val < YGraphMin then YGraphMin := Val;
end;
UpdateParentChart;
end;
{ TLine }
procedure TLine.Changed;
begin
//FIXME: not the best way of doing this
{if Visible then begin
NBPointsMax:=NBPointsMax+1;
case LineStyle of
lsHorizontal:
begin
if Position<YMinSeries then YMinSeries:=Position;
if Position>YMaxSeries then YMaxSeries:=Position;
end;
lsVertical:
begin
if Position<XMinSeries then XMinSeries:=Position;
if Position>XMaxSeries then XMaxSeries:=Position;
end;
end;
end;
end;}
case LineStyle of
lsHorizontal: begin YGraphMin := FPosGraph; YGraphMax := FPosGraph; end;
lsVertical: begin XGraphMin := FPosGraph; XGraphMax := FPosGraph; end;
end;
UpdateParentChart;
end;
constructor TLine.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FPen := TPen.Create;
FPen.OnChange := @StyleChanged;
LineStyle := lsHorizontal;
end;
destructor TLine.Destroy;
begin
inherited Destroy;
FPen.Free;
end;
procedure TLine.SetPen(AValue: TPen);
begin
FPen.Assign(AValue);
end;
procedure TLine.SetStyle(AValue: TLineStyle);
begin
if FStyle = AValue then exit;
FStyle := AValue;
Changed;
end;
procedure TLine.SetPos(AValue: Double);
begin
if FPosGraph = AValue then exit;
FPosGraph := AValue;
Changed;
end;
procedure TLine.SetSeriesColor(const AValue: TColor);
begin
FPen.Color := AValue;
end;
procedure TLine.Draw(ACanvas: TCanvas);
var
xmin, xmax, ymin, ymax, posImage: Integer;
begin
InitBounds(xmin, ymin, xmax, ymax);
ACanvas.Pen.Assign(FPen);
case LineStyle of
lsHorizontal:
if InRange(FPosGraph, ParentChart.XGraphMin, ParentChart.XGraphMax) then begin
ParentChart.YGraphToImage(FPosGraph, posImage);
ACanvas.MoveTo(xmin, posImage);
ACanvas.LineTo(xmax, posImage);
end;
lsVertical:
if InRange(FPosGraph, ParentChart.YGraphMin, ParentChart.YGraphMax) then begin
ParentChart.XGraphToImage(FPosGraph, posImage);
ACanvas.MoveTo(posImage, ymin);
ACanvas.LineTo(posImage, ymax);
end;
end;
end;
function TLine.GetSeriesColor: TColor;
begin
Result := FPen.Color;
end;
{ TBarSeries }
constructor TBarSeries.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FBarWidthPercent := 70; //70%
FBarBrush := TBrush.Create;
FBarBrush.OnChange := @StyleChanged;
FBarPen := TPen.Create;
FBarPen.OnChange := @StyleChanged;
FBarPen.Mode := pmCopy;
FBarPen.Style := psSolid;
FBarPen.Width := 1;
FBarPen.Color := clBlack;
FBarBrush.Color := clRed;
end;
destructor TBarSeries.Destroy;
begin
FBarPen.Free;
FBarBrush.Free;
inherited Destroy;
end;
procedure TBarSeries.SetBarBrush(Value: TBrush);
begin
FBarBrush.Assign(Value);
end;
procedure TBarSeries.SetBarPen(Value:TPen);
begin
FBarPen.Assign(Value);
end;
procedure TBarSeries.SetBarWidthPercent(Value: Integer);
begin
if (Value < 1) or (Value > 100) then
raise BarException.Create('Wrong BarWidth Percent')
else
FBarWidthPercent := Value;
end;
procedure TBarSeries.SetSeriesColor(const AValue: TColor);
begin
FBarBrush.Color := AValue;
end;
function TBarSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint;
begin
if Color = clTAColor then Color := SeriesColor;
Result := inherited AddXY(X, Y, XLabel, Color);
//update the interval - the 0.6 is a hack to allow the bars to have some space apart
if X > XGraphMax - 0.6 then XGraphMax := X + 0.6;
if X < XGraphMin + 0.6 then XGraphMin := X - 0.6;
//check if the bar is abouve 0 or not
if Y >= 0 then begin
if Y > YGraphMax then YGraphMax := Y;
if YGraphMin > 0 then YGraphMin := 0;
end else begin
if Y < YGraphMin then YGraphMin := Y;
if YGraphMax < 0 then YGraphMax := 0;
end;
UpdateParentChart;
end;
procedure TBarSeries.Draw(ACanvas: TCanvas);
var
XMin, XMax: Integer;
i: Integer;
graphCoordTop: TChartCoord;
graphCoordBottom: TChartCoord;
topX, topY, bottomY: Integer;
barWidth, totalbarWidth, totalBarSeries, myPos: Integer;
bx1, by1, bx2, by2: Integer;
function BarInViewPort(cTop, cBottom: TChartCoord): Boolean;
begin //FIXME make cleaner?
Result :=
((cTop.x >= ParentChart.XGraphMin) and (cTop.x <= ParentChart.XGraphMax)) and
( ((cTop.y > ParentChart.YGraphMax) and (cBottom.y < ParentChart.YGraphMin))
or ( (cTop.y < ParentChart.YGraphMax) and (cTop.y > ParentChart.YGraphMin))
or ( (cBottom.y < ParentChart.YGraphMax) and (cBottom.y > ParentChart.YGraphMin))
);
end;
begin
//no elements to draw
if FCoordList.Count = 0 then exit;
//get the limits (for the zoom) ??
XMin := ParentChart.XImageMin;
XMax := ParentChart.XImageMax;
if XMin > XMax then
Exchange(XMin, XMax);
// Draw the bars
ACanvas.Pen.Assign(FBarPen);
ACanvas.Brush.Assign(FBarBrush);
//calc the single bar width
totalbarWidth :=
Round((FBarWidthPercent * 0.01) * ParentChart.ChartWidth / FCoordList.Count);
ExamineAllBarSeries(totalBarSeries, myPos);
barWidth := totalbarWidth div totalBarSeries;
for i := 0 to FCoordList.Count - 1 do begin
//get the top and bottom points
if TChartCoord(FCoordList.Items[i]^).y >= 0 then begin
graphCoordTop := TChartCoord(FCoordList.Items[i]^);
graphCoordBottom.x := graphCoordTop.x;
graphCoordBottom.y := 0;
end else begin
graphCoordBottom := TChartCoord(FCoordList.Items[i]^);
graphCoordTop.x := graphCoordBottom.x;
graphCoordTop.y := 0;
end;
//check if bar in view port
if BarInViewPort(graphCoordTop, graphCoordBottom) then begin
//only draw to the limits
if graphCoordTop.y > ParentChart.YGraphMax then graphCoordTop.y := ParentChart.YGraphMax;
if graphCoordBottom.y < ParentChart.YGraphMin then graphCoordBottom.y := ParentChart.YGraphMin;
//convert from graph to imgs coords
ParentChart.GraphToImage(graphCoordTop.x, graphCoordTop.y, topX, topY);
ParentChart.YGraphToImage(graphCoordBottom.y, bottomY);
//calc coords for bar
{ bx1 := topX-(barWidth div 2);
by1 := topY;
bx2 := topX+(barWidth div 2);
by2 := bottomY;
}
bx1 := topX - (totalbarWidth div 2) + myPos * barWidth;
by1 := topY;
bx2 := topX - (totalbarWidth div 2) + myPos * barWidth + barWidth;
by2 := bottomY;
//FIXME only draw if bar inside image coord (get a better way of doing this)
if (bx1 >= XMin) and (bx2 <= XMax) then
if by1 = by2 then begin //draw a line when y=0 FIXME (clean)
ACanvas.Pen.Color := FBarBrush.Color;
ACanvas.MoveTo(bx1, by1);
ACanvas.LineTo(bx2, by2);
ACanvas.Pen.Assign(FBarPen);
end else
ACanvas.Rectangle( bx1, by1, bx2, by2);
end;
end; // for
end;
procedure TBarSeries.DrawLegend(ACanvas: TCanvas; const ARect: TRect);
begin
inherited DrawLegend(ACanvas, ARect);
ACanvas.Pen.Color := clBlack;
ACanvas.Brush.Assign(BarBrush);
ACanvas.Rectangle(ARect);
end;
procedure TBarSeries.ExamineAllBarSeries(out ATotalNumber, AMyPos: Integer);
var
i: Integer;
begin
ATotalNumber := 0;
AMyPos := -1;
for i := 0 to ParentChart.SeriesCount - 1 do begin
if ParentChart.Series[i] = Self then
AMyPos := ATotalNumber;
if ParentChart.Series[i] is TBarSeries then
Inc(ATotalNumber);
end;
Assert(AMyPos >= 0);
end;
function TBarSeries.GetSeriesColor: TColor;
begin
Result := FBarBrush.Color;
end;
{ TPieSeries }
function TPieSeries.AddPie(Value: Double; Text: String; Color: TColor): Longint;
begin
Result := AddXY(getXMinVal + 1, Value, Text, Color);
end;
function TPieSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint;
begin
if Color = clTAColor then Color := Colors[ColorIndex];
Inc(ColorIndex);
if ColorIndex > MaxColor then ColorIndex := 1;
Result := inherited AddXY(X, Y, XLabel, Color);
UpdateParentChart;
end;
procedure TPieSeries.AfterAdd;
begin
// disable axis when we have TPie series
ParentChart.LeftAxis.Visible := false;
ParentChart.BottomAxis.Visible := false;
end;
constructor TPieSeries.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ColorIndex := 1;
LabelBackgroundColor := clYellow;
LabelToPieLinkColor := clWhite;
end;
procedure TPieSeries.Draw(ACanvas: TCanvas);
var
i, radius: Integer;
yTotal, prevAngle, angleStep: Double;
graphCoord: PChartCoord;
labelWidths, labelHeights: TIntegerDynArray;
labelTexts: TStringDynArray;
a, b, center: TPoint;
const
MARGIN = 8;
MARKS_DIST = 32;
MarkYMargin = 2;
MarkXMargin = 4;
begin
if FCoordList.Count = 0 then exit;
yTotal := 0;
for i := 0 to FCoordList.Count - 1 do
yTotal += PChartCoord(FCoordList[i])^.y;
SetLength(labelWidths, FCoordList.Count);
SetLength(labelHeights, FCoordList.Count);
SetLength(labelTexts, FCoordList.Count);
for i := 0 to FCoordList.Count - 1 do
with PChartCoord(FCoordList[i])^ do begin
labelTexts[i] := FormattedMark(i);
with ACanvas.TextExtent(labelTexts[i]) do begin
labelWidths[i] := cx;
labelHeights[i] := cy;
end;
end;
with ParentChart do begin
center.x := (XImageMin + XImageMax) div 2;
center.y := (YImageMin + YImageMax) div 2;
// Reserve space for labels.
radius := Min(
XImageMax - center.x - MaxIntValue(labelWidths),
YImageMin - center.y - MaxIntValue(labelHeights));
end;
radius := Max(radius - MARKS_DIST - MARGIN, 0);
prevAngle := 0;
for i := 0 to FCoordList.Count - 1 do begin
// if y < 0 then y := -y;
// if y = 0 then y := 0.1; // just to simulate tchart when y=0
graphCoord := FCoordList[i];
angleStep := graphCoord^.y / yTotal * 360 * 16;
ACanvas.Brush.Color := graphCoord^.Color;
ACanvas.RadialPie(
center.x - radius, center.y - radius,
center.x + radius, center.y + radius, round(prevAngle), round(angleStep));
a := LineEndPoint(center, prevAngle + angleStep / 2, radius);
b := LineEndPoint(center, prevAngle + angleStep / 2, radius + MARKS_DIST);
// line from mark to pie
ACanvas.Pen.Color := LabelToPieLinkColor;
ACanvas.MoveTo(a.x, a.y);
ACanvas.LineTo(b.x, b.y);
if b.x < center.x then
b.x -= labelWidths[i];
if b.y < center.y then
b.y -= labelHeights[i];
ACanvas.Pen.Color := LabelTextColor;
ACanvas.Brush.Color := LabelBackgroundColor;
ACanvas.Rectangle(
b.x - MarkXMargin, b.y - MarkYMargin,
b.x + labelWidths[i] + MarkXMargin, b.y + labelHeights[i] + MarkYMargin);
ACanvas.TextOut(b.x, b.y, labelTexts[i]);
prevAngle += angleStep;
end;
end;
procedure TPieSeries.DrawLegend(ACanvas: TCanvas; const ARect: TRect);
var
i: Integer;
pc, bc: TColor;
r: TRect;
begin
r := ARect;
pc := ACanvas.Pen.Color;
bc := ACanvas.Brush.Color;
for i := 0 to Count - 1 do begin
ACanvas.Pen.Color := pc;
ACanvas.Brush.Color := bc;
with PChartCoord(Coord.Items[i])^ do begin
ACanvas.TextOut(r.Right + 3, r.Top, Format('%1.2g %s', [y, Text]));
ACanvas.Pen.Color := clBlack;
ACanvas.Brush.Color := Color;
end;
ACanvas.Rectangle(r);
OffsetRect(r, 0, r.Bottom - r.Top + LEGEND_SPACING);
end;
end;
function TPieSeries.GetLegendCount: Integer;
begin
Result := Count;
end;
function TPieSeries.GetLegendWidth(ACanvas: TCanvas): Integer;
var
i: Integer;
begin
Result := 0;
for i := 0 to Count - 1 do
with PChartCoord(Coord.Items[i])^ do
Result := Max(ACanvas.TextWidth(Format('%1.2g %s', [y, Text])), Result);
end;
function TPieSeries.GetMiscColor(AIndex: integer): TColor;
begin
Result := FMiscColors[AIndex];
end;
function TPieSeries.GetSeriesColor: TColor;
begin
Result := clBlack; // SeriesColor is meaningless for PieSeries
end;
procedure TPieSeries.SetMiscColor(AIndex: integer; const AValue: TColor);
begin
if FMiscColors[AIndex] = AValue then exit;
FMiscColors[AIndex] := AValue;
UpdateParentChart;
end;
procedure TPieSeries.SetSeriesColor(const AValue: TColor);
begin
// SeriesColor is meaningless for PieSeries
end;
{ TAreaSeries }
constructor TAreaSeries.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FAreaLinesPen := TChartPen.Create;
FAreaBrush := TBrush.Create;
end;
destructor TAreaSeries.Destroy;
begin
FAreaLinesPen.Free;
FAreaBrush.Free;
inherited Destroy;
end;
function TAreaSeries.AddXY(X, Y: Double; XLabel: String; Color: TColor): Longint;
begin
if Color = clTAColor then Color := SeriesColor;
Result := inherited AddXY(X, Y, XLabel, Color);
// Update max
if X > XGraphMax then XGraphMax := X;
if X < XGraphMin then XGraphMin := X;
if Y > YGraphMax then YGraphMax := Y;
if Y < YGraphMin then YGraphMin := Y;
UpdateParentChart;
end;
procedure TAreaSeries.SetAreaBrush(Value: TBrush);
begin
FAreaBrush.Assign(Value);
UpdateParentChart;
end;
procedure TAreaSeries.SetStairs(Value: Boolean);
begin
FStairs := Value;
UpdateParentChart;
end;
procedure TAreaSeries.SetInvertedStairs(Value: Boolean);
begin
FInvertedStairs := Value;
UpdateParentChart;
end;
procedure TAreaSeries.SetSeriesColor(const AValue: TColor);
begin
FAreaBrush.Color := AValue;
end;
procedure TAreaSeries.Draw(ACanvas: TCanvas);
var
i: Integer;
xi1, yi1, xi2, yi2, xi2a: Integer;
xg1, yg1, xg2, yg2: Double;
XMin, XMax, YMin, YMax: Integer;
graphCoord: PChartCoord;
iy_min: Integer;
procedure DrawPart;
begin
ACanvas.Polygon([
Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi2), Point(xi2, iy_min)]);
end;
begin
if Count = 0 then exit;
InitBounds(XMin, YMin, XMax, YMax);
with ParentChart do begin
Canvas.Pen.Mode := pmCopy;
Canvas.Pen.Style := psSolid;
Canvas.Pen.Width := 1;
end;
for i := 0 to Count - 2 do begin
graphCoord := FCoordList.Items[i];
xg1 := graphCoord^.x;
yg1 := graphCoord^.y;
ParentChart.GraphToImage(xg1, yg1, xi1, yi1);
graphCoord := FCoordList.Items[i + 1];
xg2 := graphCoord^.x;
yg2 := graphCoord^.y;
ParentChart.GraphToImage(xg2, yg2, xi2, yi2);
ParentChart.YGraphToImage(ParentChart.YGraphMin, iy_min);
ACanvas.Pen.Color:= clBlack;
ACanvas.Brush.Color:= graphCoord^.Color;
with ParentChart do
if // top line is totally inside the viewport
InRange(xg1, XGraphMin, XGraphMax) and
InRange(xg2, XGraphMin, XGraphMax) and
InRange(yg1, YGraphMin, YGraphMax) and
InRange(yg2, YGraphMin, YGraphMax)
then begin
if FStairs then begin
if FInvertedStairs then
ACanvas.Polygon([
Point(xi1, iy_min), Point(xi1, yi2), Point(xi2, yi2), Point(xi2, iy_min)])
else
ACanvas.Polygon([
Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi1), Point(xi2, iy_min)])
end else
DrawPart;
continue;
end;
with ParentChart do
if // top line is totally outside the viewport
(xg1 < XGraphMin) and (xg2 < XGraphMin) or
(xg1 > XGraphMax) and (xg2 > XGraphMax) or
(yg1 < YGraphMin) and (yg2 < YGraphMin)
then
continue;
if yg1 > yg2 then begin
Exchange(xg1, xg2); Exchange(yg1, yg2);
Exchange(xi1, xi2); Exchange(yi1, yi2);
end;
if yg1 = yg2 then begin
if xg1 > xg2 then begin
Exchange(xg1, xg2);
Exchange(yg1, yg2);
end;
if xg1 < ParentChart.XGraphMin then xi1 := ParentChart.XImageMin;
if xg2 > ParentChart.XGraphMax then xi2 := ParentChart.XImageMax;
end
else if xg1 = xg2 then begin
if yg1 < ParentChart.YGraphMin then yi1 := ParentChart.YImageMin;
if yg2 > ParentChart.YGraphMax then yi2 := ParentChart.YImageMax;
end
else if ParentChart.LineInViewPort(xg1, yg1, xg2, yg2) then begin
xi2a := xi2;
ParentChart.GraphToImage(xg1, yg1, xi1, yi1);
ParentChart.GraphToImage(xg2, yg2, xi2, yi2);
if yi2 <= YMin then begin
ACanvas.Polygon([
Point(xi1, iy_min), Point(xi1, yi1), Point(xi2, yi2),
Point(xi2a, YMin), Point(xi2a, iy_min)]);
continue;
end;
end
else if yg2 >= ParentChart.YGraphMax then begin
yi1 := YMin;
yi2 := YMin;
xi1 := EnsureRange(xi1, XMin, XMax);
xi2 := EnsureRange(xi2, XMin, XMax);
end;
DrawPart;
end;
end;
procedure TAreaSeries.DrawLegend(ACanvas: TCanvas; const ARect: TRect);
begin
inherited DrawLegend(ACanvas, ARect);
ACanvas.Pen.Color := clBlack;
ACanvas.Brush.Color := SeriesColor;
ACanvas.Rectangle(ARect);
end;
function TAreaSeries.GetSeriesColor: TColor;
begin
Result := FAreaBrush.Color;
end;
{ TBasicLineSeries }
procedure TBasicLineSeries.DrawLegend(ACanvas: TCanvas; const ARect: TRect);
var
y: Integer;
begin
inherited DrawLegend(ACanvas, ARect);
ACanvas.Pen.Color := SeriesColor;
y := (ARect.Top + ARect.Bottom) div 2;
ACanvas.MoveTo(ARect.Left, y);
ACanvas.LineTo(ARect.Right, y);
end;
initialization
RegisterSeriesClass(TSerie, 'Line series');
RegisterSeriesClass(TAreaSeries, 'Area series');
RegisterSeriesClass(TBarSeries, 'Bar series');
RegisterSeriesClass(TPieSeries, 'Pie series');
RegisterSeriesClass(TLine, 'Line');
end.