mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-09-06 10:00:34 +02:00
TAChart: Initial implementation of error bars for TLineSeries, TCubicSplineSeries, TBSplineSeries and TFitSeries.
git-svn-id: trunk@58597 -
This commit is contained in:
parent
12955c9ea1
commit
1168572c7d
@ -22,6 +22,7 @@ uses
|
|||||||
|
|
||||||
const
|
const
|
||||||
DEF_AXIS_INDEX = -1;
|
DEF_AXIS_INDEX = -1;
|
||||||
|
DEF_ERR_ENDLENGTH = 5;
|
||||||
|
|
||||||
type
|
type
|
||||||
TNearestPointParams = record
|
TNearestPointParams = record
|
||||||
@ -257,9 +258,12 @@ type
|
|||||||
TBasicPointSeries = class(TChartSeries)
|
TBasicPointSeries = class(TChartSeries)
|
||||||
strict private
|
strict private
|
||||||
FMarkPositions: TLinearMarkPositions;
|
FMarkPositions: TLinearMarkPositions;
|
||||||
|
FErrorBars: array[0..1] of TChartErrorBar;
|
||||||
FOnCustomDrawPointer: TSeriesPointerCustomDrawEvent;
|
FOnCustomDrawPointer: TSeriesPointerCustomDrawEvent;
|
||||||
FOnGetPointerStyle: TSeriesPointerStyleEvent;
|
FOnGetPointerStyle: TSeriesPointerStyleEvent;
|
||||||
|
function GetErrorBars(AIndex: Integer): TChartErrorBar;
|
||||||
function GetLabelDirection(AIndex: Integer): TLabelDirection;
|
function GetLabelDirection(AIndex: Integer): TLabelDirection;
|
||||||
|
procedure SetErrorBars(AIndex: Integer; AValue: TChartErrorBar);
|
||||||
procedure SetMarkPositions(AValue: TLinearMarkPositions);
|
procedure SetMarkPositions(AValue: TLinearMarkPositions);
|
||||||
procedure SetPointer(AValue: TSeriesPointer);
|
procedure SetPointer(AValue: TSeriesPointer);
|
||||||
procedure SetStacked(AValue: Boolean);
|
procedure SetStacked(AValue: Boolean);
|
||||||
@ -276,11 +280,16 @@ type
|
|||||||
|
|
||||||
procedure AfterDrawPointer(
|
procedure AfterDrawPointer(
|
||||||
ADrawer: IChartDrawer; AIndex: Integer; const APos: TPoint); virtual;
|
ADrawer: IChartDrawer; AIndex: Integer; const APos: TPoint); virtual;
|
||||||
|
procedure DrawErrorBars(ADrawer: IChartDrawer);
|
||||||
procedure DrawLabels(ADrawer: IChartDrawer);
|
procedure DrawLabels(ADrawer: IChartDrawer);
|
||||||
procedure DrawPointers(ADrawer: IChartDrawer; AStyleIndex: Integer = 0;
|
procedure DrawPointers(ADrawer: IChartDrawer; AStyleIndex: Integer = 0;
|
||||||
UseDataColors: Boolean = false);
|
UseDataColors: Boolean = false);
|
||||||
procedure FindExtentInterval(
|
procedure FindExtentInterval(
|
||||||
const AExtent: TDoubleRect; AFilterByExtent: Boolean);
|
const AExtent: TDoubleRect; AFilterByExtent: Boolean);
|
||||||
|
{
|
||||||
|
function GetErrorBars(APointIndex: Integer; IsXError: Boolean;
|
||||||
|
out AGraphPointPos, AGraphPointNeg: Double): Boolean;
|
||||||
|
}
|
||||||
function GetLabelDataPoint(AIndex: Integer): TDoublePoint; virtual;
|
function GetLabelDataPoint(AIndex: Integer): TDoublePoint; virtual;
|
||||||
procedure GetLegendItemsRect(AItems: TChartLegendItems; ABrush: TBrush);
|
procedure GetLegendItemsRect(AItems: TChartLegendItems; ABrush: TBrush);
|
||||||
function GetXRange(AX: Double; AIndex: Integer): Double;
|
function GetXRange(AX: Double; AIndex: Integer): Double;
|
||||||
@ -296,9 +305,13 @@ type
|
|||||||
|
|
||||||
property Pointer: TSeriesPointer read FPointer write SetPointer;
|
property Pointer: TSeriesPointer read FPointer write SetPointer;
|
||||||
property Stacked: Boolean read FStacked write SetStacked;
|
property Stacked: Boolean read FStacked write SetStacked;
|
||||||
|
|
||||||
protected
|
protected
|
||||||
procedure AfterAdd; override;
|
procedure AfterAdd; override;
|
||||||
procedure UpdateMargins(ADrawer: IChartDrawer; var AMargins: TRect); override;
|
procedure UpdateMargins(ADrawer: IChartDrawer; var AMargins: TRect); override;
|
||||||
|
|
||||||
|
property XErrorBars: TChartErrorBar index 0 read GetErrorBars write SetErrorBars;
|
||||||
|
property YErrorBars: TChartErrorBar index 1 read GetErrorBars write SetErrorBars;
|
||||||
property OnCustomDrawPointer: TSeriesPointerCustomDrawEvent
|
property OnCustomDrawPointer: TSeriesPointerCustomDrawEvent
|
||||||
read FOnCustomDrawPointer write FOnCustomDrawPointer;
|
read FOnCustomDrawPointer write FOnCustomDrawPointer;
|
||||||
property OnGetPointerStyle: TSeriesPointerStyleEvent
|
property OnGetPointerStyle: TSeriesPointerStyleEvent
|
||||||
@ -1076,10 +1089,15 @@ end;
|
|||||||
{ TBasicPointSeries }
|
{ TBasicPointSeries }
|
||||||
|
|
||||||
procedure TBasicPointSeries.AfterAdd;
|
procedure TBasicPointSeries.AfterAdd;
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
inherited AfterAdd;
|
inherited AfterAdd;
|
||||||
if Pointer <> nil then
|
if Pointer <> nil then
|
||||||
Pointer.SetOwner(ParentChart);
|
Pointer.SetOwner(ParentChart);
|
||||||
|
for i := 0 to 1 do
|
||||||
|
if FErrorBars[i] <> nil then
|
||||||
|
FErrorBars[i].SetOwner(ParentChart);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TBasicPointSeries.AfterDrawPointer(
|
procedure TBasicPointSeries.AfterDrawPointer(
|
||||||
@ -1105,16 +1123,96 @@ end;
|
|||||||
constructor TBasicPointSeries.Create(AOwner: TComponent);
|
constructor TBasicPointSeries.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
inherited;
|
inherited;
|
||||||
|
FErrorBars[0] := TChartErrorBar.Create(FChart);
|
||||||
|
FErrorBars[1] := TChartErrorBar.Create(FChart);
|
||||||
FOptimizeX := true;
|
FOptimizeX := true;
|
||||||
ToolTargets := [nptPoint, nptYList];
|
ToolTargets := [nptPoint, nptYList];
|
||||||
end;
|
end;
|
||||||
|
|
||||||
destructor TBasicPointSeries.Destroy;
|
destructor TBasicPointSeries.Destroy;
|
||||||
begin
|
begin
|
||||||
|
FreeAndNil(FErrorBars[0]);
|
||||||
|
FreeAndNil(FErrorBars[1]);
|
||||||
FreeAndNil(FPointer);
|
FreeAndNil(FPointer);
|
||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TBasicPointSeries.DrawErrorBars(ADrawer: IChartDrawer);
|
||||||
|
|
||||||
|
procedure EndBar(p: TPoint; w: Integer; IsHorBar: Boolean);
|
||||||
|
begin
|
||||||
|
if IsHorBar then
|
||||||
|
ADrawer.Line(Point(p.x, p.y-w), Point(p.x, p.y+w))
|
||||||
|
else
|
||||||
|
ADrawer.Line(Point(p.x-w, p.y), Point(p.x+w, p.y));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure DrawErrorBar(p: TDoublePoint; vp, vn: Double; w: Integer;
|
||||||
|
IsXError: Boolean);
|
||||||
|
var
|
||||||
|
p1, p2: TDoublePoint;
|
||||||
|
imgPt1, imgPt2: TPoint;
|
||||||
|
isHorBar: Boolean;
|
||||||
|
begin
|
||||||
|
isHorBar := (IsXError and not IsRotated) or (IsRotated and not IsXError);
|
||||||
|
|
||||||
|
if IsHorBar then begin
|
||||||
|
p1 := DoublePoint(vp, p.Y);
|
||||||
|
p2 := DoublePoint(vn, p.Y);
|
||||||
|
end else begin
|
||||||
|
p1 := DoublePoint(p.X, vp);
|
||||||
|
p2 := DoublePoint(p.X, vn);
|
||||||
|
end;
|
||||||
|
imgPt1 := ParentChart.GraphToImage(p1);
|
||||||
|
imgPt2 := ParentChart.GraphToImage(p2);
|
||||||
|
ADrawer.Line(imgPt1, imgPt2);
|
||||||
|
|
||||||
|
EndBar(imgPt1, w, isHorBar);
|
||||||
|
EndBar(imgPt2, w, isHorBar);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure InternalDrawErrorBars(IsXError: Boolean);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
|
p: TDoublePoint;
|
||||||
|
vp, vn: Double;
|
||||||
|
w, w0: Integer;
|
||||||
|
errbar: TChartErrorBar;
|
||||||
|
begin
|
||||||
|
if Assigned(Pointer) then
|
||||||
|
w0 := IfThen(IsXError, Pointer.VertSize, Pointer.HorizSize)
|
||||||
|
else
|
||||||
|
w0 := DEF_ERR_ENDLENGTH;
|
||||||
|
errbar := TChartErrorBar(IfThen(IsXError, XErrorBars, YErrorBars));
|
||||||
|
w := ADrawer.Scale(IfThen(errBar.Width = -1, w0, errBar.Width));
|
||||||
|
|
||||||
|
for i := FLoBound to FUpBound do begin
|
||||||
|
p := FGraphPoints[i - FLoBound];
|
||||||
|
if not ParentChart.IsPointInViewPort(p) then continue;
|
||||||
|
if IsXError then begin
|
||||||
|
if Source.GetXErrorBarLimits(i, vp, vn) then
|
||||||
|
DrawErrorBar(p, AxisToGraphX(vp), AxisToGraphX(vn), w, true);
|
||||||
|
end else begin
|
||||||
|
if Source.GetYErrorBarLimits(i, vp, vn) then
|
||||||
|
DrawErrorBar(p, AxisTographY(vp), AxisToGraphY(vn), w, false);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
begin
|
||||||
|
if Assigned(YErrorBars) and YErrorBars.Visible and Source.HasYErrorBars then
|
||||||
|
begin
|
||||||
|
ADrawer.Pen := YErrorBars.Pen;
|
||||||
|
InternalDrawErrorBars(false);
|
||||||
|
end;
|
||||||
|
|
||||||
|
if Assigned(XErrorBars) and XErrorBars.Visible and Source.HasXErrorBars then
|
||||||
|
begin
|
||||||
|
ADrawer.pen := XErrorBars.Pen;
|
||||||
|
InternalDrawErrorBars(true);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TBasicPointSeries.DrawLabels(ADrawer: IChartDrawer);
|
procedure TBasicPointSeries.DrawLabels(ADrawer: IChartDrawer);
|
||||||
var
|
var
|
||||||
prevLabelPoly: TPointArray;
|
prevLabelPoly: TPointArray;
|
||||||
@ -1272,6 +1370,11 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TBasicPointSeries.GetErrorBars(AIndex: Integer): TChartErrorBar;
|
||||||
|
begin
|
||||||
|
Result := FErrorBars[AIndex];
|
||||||
|
end;
|
||||||
|
|
||||||
function TBasicPointSeries.GetLabelDataPoint(AIndex: Integer): TDoublePoint;
|
function TBasicPointSeries.GetLabelDataPoint(AIndex: Integer): TDoublePoint;
|
||||||
begin
|
begin
|
||||||
Result := GetGraphPoint(AIndex);
|
Result := GetGraphPoint(AIndex);
|
||||||
@ -1518,6 +1621,13 @@ begin
|
|||||||
FGraphPoints[i - FLoBound] := GetGraphPoint(i);
|
FGraphPoints[i - FLoBound] := GetGraphPoint(i);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TBasicPointSeries.SetErrorBars(AIndex: Integer;
|
||||||
|
AValue: TChartErrorBar);
|
||||||
|
begin
|
||||||
|
FErrorBars[AIndex] := AValue;
|
||||||
|
UpdateParentChart;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TBasicPointSeries.SetMarkPositions(AValue: TLinearMarkPositions);
|
procedure TBasicPointSeries.SetMarkPositions(AValue: TLinearMarkPositions);
|
||||||
begin
|
begin
|
||||||
if FMarkPositions = AValue then exit;
|
if FMarkPositions = AValue then exit;
|
||||||
|
@ -149,8 +149,32 @@ type
|
|||||||
property Current: PChartDataItem read GetCurrent;
|
property Current: PChartDataItem read GetCurrent;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TChartErrorBarKind = (ebkNone, ebkConst, ebkPercent, ebkChartSource);
|
||||||
|
|
||||||
|
TChartErrorBarData = class(TPersistent)
|
||||||
|
private
|
||||||
|
FKind: TChartErrorBarKind;
|
||||||
|
FValue: array[0..1] of Double; // 0 = positive, 1 = negative
|
||||||
|
FOnChange: TNotifyEvent;
|
||||||
|
function GetValue(AIndex: Integer): Double;
|
||||||
|
procedure SetKind(AValue: TChartErrorbarKind);
|
||||||
|
procedure SetValue(AIndex: Integer; AValue: Double);
|
||||||
|
public
|
||||||
|
constructor Create;
|
||||||
|
procedure Assign(ASource: TPersistent); override;
|
||||||
|
property OnChange: TNotifyEvent read FOnChange write FOnChange;
|
||||||
|
published
|
||||||
|
property Kind: TChartErrorBarKind read FKind write SetKind default ebkNone;
|
||||||
|
property NegDelta: Double index 1 read GetValue write SetValue;
|
||||||
|
property PosDelta: Double index 0 read GetValue write SetValue;
|
||||||
|
end;
|
||||||
|
|
||||||
TCustomChartSource = class(TBasicChartSource)
|
TCustomChartSource = class(TBasicChartSource)
|
||||||
strict private
|
strict private
|
||||||
|
FErrorBarData: array[0..1] of TChartErrorBarData;
|
||||||
|
procedure ChangeErrorBarHandler(Sender: TObject);
|
||||||
|
function GetErrorBarData(AIndex: Integer): TChartErrorBarData;
|
||||||
|
procedure SetErrorBarData(AIndex: Integer; AValue: TChartErrorBarData);
|
||||||
procedure SortValuesInRange(
|
procedure SortValuesInRange(
|
||||||
var AValues: TChartValueTextArray; AStart, AEnd: Integer);
|
var AValues: TChartValueTextArray; AStart, AEnd: Integer);
|
||||||
strict protected
|
strict protected
|
||||||
@ -160,14 +184,21 @@ type
|
|||||||
FValuesTotalIsValid: Boolean;
|
FValuesTotalIsValid: Boolean;
|
||||||
FXCount: Cardinal;
|
FXCount: Cardinal;
|
||||||
FYCount: Cardinal;
|
FYCount: Cardinal;
|
||||||
|
|
||||||
function GetCount: Integer; virtual; abstract;
|
function GetCount: Integer; virtual; abstract;
|
||||||
|
function GetErrorBarLimits(APointIndex: Integer; Which: Integer;
|
||||||
|
out AUpperLimit, ALowerLimit: Double): Boolean;
|
||||||
|
function GetHasErrorBars(Which: Integer): Boolean;
|
||||||
function GetItem(AIndex: Integer): PChartDataItem; virtual; abstract;
|
function GetItem(AIndex: Integer): PChartDataItem; virtual; abstract;
|
||||||
procedure InvalidateCaches;
|
procedure InvalidateCaches;
|
||||||
procedure SetXCount(AValue: Cardinal); virtual; abstract;
|
procedure SetXCount(AValue: Cardinal); virtual; abstract;
|
||||||
procedure SetYCount(AValue: Cardinal); virtual; abstract;
|
procedure SetYCount(AValue: Cardinal); virtual; abstract;
|
||||||
|
property XErrorBarData: TChartErrorBarData index 0 read GetErrorBarData
|
||||||
|
write SetErrorBarData;
|
||||||
|
property YErrorBarData: TChartErrorBarData index 1 read GetErrorBarData
|
||||||
|
write SetErrorBarData;
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
public
|
public
|
||||||
procedure AfterDraw; virtual;
|
procedure AfterDraw; virtual;
|
||||||
procedure BeforeDraw; virtual;
|
procedure BeforeDraw; virtual;
|
||||||
@ -183,6 +214,14 @@ type
|
|||||||
function FormatItemXYText(
|
function FormatItemXYText(
|
||||||
const AFormat: String; AX, AY: Double; AText: String): String;
|
const AFormat: String; AX, AY: Double; AText: String): String;
|
||||||
function GetEnumerator: TCustomChartSourceEnumerator;
|
function GetEnumerator: TCustomChartSourceEnumerator;
|
||||||
|
function GetXErrorBarLimits(APointIndex: Integer;
|
||||||
|
out AUpperLimit, ALowerLimit: Double): Boolean;
|
||||||
|
function GetYErrorBarLimits(APointIndex: Integer;
|
||||||
|
out AUpperLimit, ALowerLimit: Double): Boolean;
|
||||||
|
function HasXErrorBars: Boolean;
|
||||||
|
function HasYErrorBars: Boolean;
|
||||||
|
function IsXErrorIndex(AXIndex: Integer): Boolean;
|
||||||
|
function IsYErrorIndex(AYIndex: Integer): Boolean;
|
||||||
function IsSorted: Boolean; virtual;
|
function IsSorted: Boolean; virtual;
|
||||||
procedure ValuesInRange(
|
procedure ValuesInRange(
|
||||||
AParams: TValuesInRangeParams; var AValues: TChartValueTextArray); virtual;
|
AParams: TValuesInRangeParams; var AValues: TChartValueTextArray); virtual;
|
||||||
@ -474,7 +513,8 @@ begin
|
|||||||
YList[AIndex - 1] := AValue;
|
YList[AIndex - 1] := AValue;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ TBaisChartSource }
|
|
||||||
|
{ TBasicChartSource }
|
||||||
|
|
||||||
constructor TBasicChartSource.Create(AOwner: TComponent);
|
constructor TBasicChartSource.Create(AOwner: TComponent);
|
||||||
begin
|
begin
|
||||||
@ -647,6 +687,45 @@ begin
|
|||||||
FIndex := 0;
|
FIndex := 0;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
{ TChartErrorBarData }
|
||||||
|
|
||||||
|
constructor TChartErrorBarData.Create;
|
||||||
|
begin
|
||||||
|
inherited;
|
||||||
|
FValue[0] := -1;
|
||||||
|
FValue[1] := -1;
|
||||||
|
FKind := ebkNone;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TChartErrorBarData.Assign(ASource: TPersistent);
|
||||||
|
begin
|
||||||
|
if ASource is TChartErrorBarData then begin
|
||||||
|
FValue := TChartErrorBarData(ASource).FValue;
|
||||||
|
FKind := TChartErrorBarData(ASource).Kind;
|
||||||
|
end;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TChartErrorBarData.GetValue(AIndex: Integer): Double;
|
||||||
|
begin
|
||||||
|
Result := FValue[AIndex];
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TChartErrorBarData.SetKind(AValue: TChartErrorBarKind);
|
||||||
|
begin
|
||||||
|
if FKind = AValue then exit;
|
||||||
|
FKind := AValue;
|
||||||
|
if Assigned(FOnChange) then FOnChange(self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TChartErrorBarData.SetValue(AIndex: Integer; AValue: Double);
|
||||||
|
begin
|
||||||
|
FValue[AIndex] := AValue;
|
||||||
|
if Assigned(FOnChange) then FOnChange(self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TCustomChartSource }
|
{ TCustomChartSource }
|
||||||
|
|
||||||
procedure TCustomChartSource.AfterDraw;
|
procedure TCustomChartSource.AfterDraw;
|
||||||
@ -665,10 +744,29 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
constructor TCustomChartSource.Create(AOwner: TComponent);
|
constructor TCustomChartSource.Create(AOwner: TComponent);
|
||||||
|
var
|
||||||
|
i: Integer;
|
||||||
begin
|
begin
|
||||||
inherited Create(AOwner);
|
inherited Create(AOwner);
|
||||||
FXCount := 1;
|
FXCount := 1;
|
||||||
FYCount := 1;
|
FYCount := 1;
|
||||||
|
for i:=0 to 1 do begin
|
||||||
|
FErrorBarData[i] := TChartErrorBarData.Create;
|
||||||
|
FErrorBarData[i].OnChange := @ChangeErrorBarHandler;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TCustomChartSource.Destroy;
|
||||||
|
begin
|
||||||
|
FErrorBarData[0].Free;
|
||||||
|
FErrorBarData[1].Free;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCustomChartSource.ChangeErrorBarHandler(Sender: TObject);
|
||||||
|
begin
|
||||||
|
Unused(Sender);
|
||||||
|
Notify;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TCustomChartSource.EndUpdate;
|
procedure TCustomChartSource.EndUpdate;
|
||||||
@ -683,45 +781,96 @@ end;
|
|||||||
function TCustomChartSource.Extent: TDoubleRect;
|
function TCustomChartSource.Extent: TDoubleRect;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
vhi, vlo: Double;
|
||||||
begin
|
begin
|
||||||
if FExtentIsValid then exit(FExtent);
|
if FExtentIsValid then exit(FExtent);
|
||||||
FExtent := EmptyExtent;
|
FExtent := EmptyExtent;
|
||||||
|
|
||||||
|
if HasXErrorBars then
|
||||||
|
for i := 0 to Count - 1 do begin
|
||||||
|
GetXErrorBarLimits(i, vhi, vlo);
|
||||||
|
UpdateMinMax(vhi, FExtent.a.X, FExtent.b.X);
|
||||||
|
UpdateMinMax(vlo, FExtent.a.X, FExtent.b.X);
|
||||||
|
end
|
||||||
|
else
|
||||||
for i:=0 to Count - 1 do
|
for i:=0 to Count - 1 do
|
||||||
with Item[i]^ do begin
|
UpdateMinMax(Item[i]^.X, FExtent.a.X, FExtent.b.X);
|
||||||
UpdateMinMax(X, FExtent.a.X, FExtent.b.X);
|
|
||||||
UpdateMinMax(Y, FExtent.a.Y, FExtent.b.Y);
|
if HasYErrorBars then
|
||||||
end;
|
for i := 0 to Count - 1 do begin
|
||||||
|
GetYErrorBarLimits(i, vhi, vlo);
|
||||||
|
UpdateMinMax(vhi, FExtent.a.Y, FExtent.b.Y);
|
||||||
|
UpdateMinMax(vlo, FExtent.a.Y, FExtent.b.Y);
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for i:=0 to Count - 1 do
|
||||||
|
UpdateMinMax(Item[i]^.Y, FExtent.a.Y, FExtent.b.Y);
|
||||||
|
|
||||||
FExtentIsValid := true;
|
FExtentIsValid := true;
|
||||||
Result := FExtent;
|
Result := FExtent;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Calculates the extent of multiple y values stacked onto each other. }
|
||||||
function TCustomChartSource.ExtentCumulative: TDoubleRect;
|
function TCustomChartSource.ExtentCumulative: TDoubleRect;
|
||||||
var
|
var
|
||||||
h: Double;
|
h: Double;
|
||||||
i, j: Integer;
|
i, j: Integer;
|
||||||
|
jyp: Integer = -1;
|
||||||
|
jyn: Integer = -1;
|
||||||
begin
|
begin
|
||||||
Result := Extent;
|
Result := Extent;
|
||||||
if YCount < 2 then exit;
|
if YCount < 2 then exit;
|
||||||
|
|
||||||
|
// Skip the y values used for error bars in calculating the cumulative sum.
|
||||||
|
if YErrorBarData.Kind = ebkChartSource then begin
|
||||||
|
jyp := round(YErrorBarData.PosDelta) - 1; // -1 because YList index is offset by 1
|
||||||
|
jyn := round(YErrorBarData.NegDelta) - 1;
|
||||||
|
end;
|
||||||
|
|
||||||
for i := 0 to Count - 1 do begin
|
for i := 0 to Count - 1 do begin
|
||||||
h := NumberOr(Item[i]^.Y);
|
h := NumberOr(Item[i]^.Y);
|
||||||
for j := 0 to YCount - 2 do begin
|
for j := 0 to YCount - 2 do
|
||||||
|
if (j <> jyp) and (j <> jyn) then begin
|
||||||
h += NumberOr(Item[i]^.YList[j]);
|
h += NumberOr(Item[i]^.YList[j]);
|
||||||
// If some of Y values are negative, h may be non-monotonic.
|
// If some of the Y values are negative, h may be non-monotonic.
|
||||||
UpdateMinMax(h, Result.a.Y, Result.b.Y);
|
UpdateMinMax(h, Result.a.Y, Result.b.Y);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Calculates the extent including multiple x and y values (non-stacked) }
|
||||||
function TCustomChartSource.ExtentList: TDoubleRect;
|
function TCustomChartSource.ExtentList: TDoubleRect;
|
||||||
var
|
var
|
||||||
i, j: Integer;
|
i, j: Integer;
|
||||||
|
jxp, jxn: Integer;
|
||||||
|
jyp, jyn: Integer;
|
||||||
begin
|
begin
|
||||||
Result := Extent;
|
Result := Extent;
|
||||||
|
if (YCount < 2) and (XCount < 2) then exit;
|
||||||
|
|
||||||
|
// Skip then x and y values used for error bars when calculating the list extent.
|
||||||
|
if XErrorBarData.Kind = ebkChartSource then begin
|
||||||
|
jxp := round(XErrorBarData.PosDelta) - 1; // -1 because XList is offset by 1
|
||||||
|
jxn := round(XErrorBarData.NegDelta) - 1;
|
||||||
|
end else begin
|
||||||
|
jxp := -1;
|
||||||
|
jyp := -1;
|
||||||
|
end;
|
||||||
|
if YErrorBarData.Kind = ebkChartSource then begin
|
||||||
|
jyp := round(YErrorbarData.PosDelta) - 1; // -1 because YList is offset by 1
|
||||||
|
jyn := round(YErrorBarData.NegDelta) - 1;
|
||||||
|
end else begin
|
||||||
|
jyp := -1;
|
||||||
|
jyn := -1;
|
||||||
|
end;
|
||||||
|
|
||||||
for i := 0 to Count - 1 do
|
for i := 0 to Count - 1 do
|
||||||
with Item[i]^ do begin
|
with Item[i]^ do begin
|
||||||
for j := 0 to High(XList) do
|
for j := 0 to High(XList) do
|
||||||
|
if (j <> jxp) and (j <> jxn) then
|
||||||
UpdateMinMax(XList[j], Result.a.X, Result.b.X);
|
UpdateMinMax(XList[j], Result.a.X, Result.b.X);
|
||||||
for j := 0 to High(YList) do
|
for j := 0 to High(YList) do
|
||||||
|
if (j <> jyp) and (j <> jyn) then
|
||||||
UpdateMinMax(YList[j], Result.a.Y, Result.b.Y);
|
UpdateMinMax(YList[j], Result.a.Y, Result.b.Y);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
@ -805,17 +954,140 @@ begin
|
|||||||
Result := TCustomChartSourceEnumerator.Create(Self);
|
Result := TCustomChartSourceEnumerator.Create(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.GetErrorbarData(AIndex: Integer): TChartErrorBarData;
|
||||||
|
begin
|
||||||
|
Result := FErrorBarData[AIndex];
|
||||||
|
end;
|
||||||
|
|
||||||
|
{ Returns the error bar limits in positive and negative direction for the
|
||||||
|
x (which = 0) or y (which = 1) coordinates of the data point at the specified
|
||||||
|
index. The result is false if there is no error bar. }
|
||||||
|
function TCustomChartSource.GetErrorBarLimits(APointIndex: Integer;
|
||||||
|
Which: Integer; out AUpperLimit, ALowerLimit: Double): Boolean;
|
||||||
|
var
|
||||||
|
v: Double;
|
||||||
|
deltaP, deltaN: Double;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
|
||||||
|
if Which = 0 then
|
||||||
|
v := Item[APointIndex]^.X
|
||||||
|
else
|
||||||
|
v := Item[APointIndex]^.Y;
|
||||||
|
|
||||||
|
AUpperLimit := v;
|
||||||
|
ALowerLimit := v;
|
||||||
|
|
||||||
|
if IsNaN(v) then
|
||||||
|
exit;
|
||||||
|
|
||||||
|
if Assigned(FErrorBarData[Which]) then begin
|
||||||
|
case FErrorBarData[Which].Kind of
|
||||||
|
ebkNone:
|
||||||
|
exit;
|
||||||
|
ebkConst:
|
||||||
|
begin
|
||||||
|
deltaP := FErrorBarData[Which].PosDelta;
|
||||||
|
if FErrorBarData[Which].NegDelta = -1 then
|
||||||
|
deltaN := deltaP
|
||||||
|
else
|
||||||
|
deltaN := FErrorBarData[Which].NegDelta;
|
||||||
|
end;
|
||||||
|
ebkPercent:
|
||||||
|
begin
|
||||||
|
deltaP := v * FErrorBarData[Which].PosDelta * PERCENT;
|
||||||
|
if FErrorBarData[Which].NegDelta = -1 then
|
||||||
|
deltaN := deltaP
|
||||||
|
else
|
||||||
|
deltaN := v * FErrorBarData[Which].NegDelta * PERCENT;
|
||||||
|
end;
|
||||||
|
ebkChartSource:
|
||||||
|
begin
|
||||||
|
if Which = 0 then
|
||||||
|
deltaP := Item[APointIndex]^.GetX(round(FErrorbarData[0].PosDelta))
|
||||||
|
else
|
||||||
|
deltaP := Item[APointIndex]^.GetY(round(FErrorBarData[1].PosDelta));
|
||||||
|
if FErrorBarData[Which].NegDelta = -1 then
|
||||||
|
deltaN := deltaP
|
||||||
|
else
|
||||||
|
if Which = 0 then
|
||||||
|
deltaN := Item[APointIndex]^.GetX(round(FErrorBarData[Which].NegDelta))
|
||||||
|
else
|
||||||
|
deltaN := Item[APointIndex]^.GetY(round(FErrorBarData[Which].NegDelta));
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
AUpperLimit := v + abs(deltaP);
|
||||||
|
ALowerLimit := v - abs(deltaN);
|
||||||
|
Result := true;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.GetXErrorBarLimits(APointIndex: Integer;
|
||||||
|
out AUpperLimit, ALowerLimit: Double): Boolean;
|
||||||
|
begin
|
||||||
|
Result := GetErrorBarLimits(APointIndex, 0, AUpperLimit, ALowerLimit);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.GetYErrorBarLimits(APointIndex: Integer;
|
||||||
|
out AUpperLimit, ALowerLimit: Double): Boolean;
|
||||||
|
begin
|
||||||
|
Result := GetErrorBarLimits(APointIndex, 1, AUpperLimit, ALowerLimit);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.GetHasErrorBars(Which: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := false;
|
||||||
|
if Assigned(FErrorBarData[Which]) then
|
||||||
|
case FErrorBarData[Which].Kind of
|
||||||
|
ebkNone:
|
||||||
|
;
|
||||||
|
ebkConst, ebkPercent:
|
||||||
|
Result := (FErrorBarData[Which].PosDelta > 0);
|
||||||
|
ebkChartSource:
|
||||||
|
Result := (FErrorBarData[Which].PosDelta > -1);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.HasXErrorBars: Boolean;
|
||||||
|
begin
|
||||||
|
Result := GetHasErrorBars(0);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.HasYErrorBars: Boolean;
|
||||||
|
begin
|
||||||
|
Result := GetHasErrorBars(1);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TCustomChartSource.InvalidateCaches;
|
procedure TCustomChartSource.InvalidateCaches;
|
||||||
begin
|
begin
|
||||||
FExtentIsValid := false;
|
FExtentIsValid := false;
|
||||||
FValuesTotalIsValid := false;
|
FValuesTotalIsValid := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.IsXErrorIndex(AXIndex: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (XErrorBarData.Kind = ebkChartSource) and
|
||||||
|
((round(XErrorBarData.PosDelta) = AXIndex) or (round(XErrorBarData.NegDelta) = AXIndex));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCustomChartSource.IsYErrorIndex(AYIndex: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (YErrorBarData.Kind = ebkChartSource) and
|
||||||
|
((round(YErrorBarData.PosDelta) = AYIndex) or (round(YErrorBarData.NegDelta) = AYIndex));
|
||||||
|
end;
|
||||||
|
|
||||||
function TCustomChartSource.IsSorted: Boolean;
|
function TCustomChartSource.IsSorted: Boolean;
|
||||||
begin
|
begin
|
||||||
Result := false;
|
Result := false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TCustomChartSource.SetErrorbarData(AIndex: Integer;
|
||||||
|
AValue: TChartErrorBarData);
|
||||||
|
begin
|
||||||
|
FErrorbarData[AIndex] := AValue;
|
||||||
|
Notify;
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TCustomChartSource.SortValuesInRange(
|
procedure TCustomChartSource.SortValuesInRange(
|
||||||
var AValues: TChartValueTextArray; AStart, AEnd: Integer);
|
var AValues: TChartValueTextArray; AStart, AEnd: Integer);
|
||||||
var
|
var
|
||||||
|
@ -183,6 +183,8 @@ type
|
|||||||
property Step: TFuncSeriesStep
|
property Step: TFuncSeriesStep
|
||||||
read FStep write SetStep default DEF_SPLINE_STEP;
|
read FStep write SetStep default DEF_SPLINE_STEP;
|
||||||
property ToolTargets default [nptPoint, nptCustom];
|
property ToolTargets default [nptPoint, nptCustom];
|
||||||
|
property XErrorBars;
|
||||||
|
property YErrorBars;
|
||||||
property OnCustomDrawPointer;
|
property OnCustomDrawPointer;
|
||||||
property OnGetPointerStyle;
|
property OnGetPointerStyle;
|
||||||
end;
|
end;
|
||||||
@ -271,6 +273,8 @@ type
|
|||||||
read FSplineType write SetSplineType default cstNatural;
|
read FSplineType write SetSplineType default cstNatural;
|
||||||
property Step: TFuncSeriesStep
|
property Step: TFuncSeriesStep
|
||||||
read FStep write SetStep default DEF_SPLINE_STEP;
|
read FStep write SetStep default DEF_SPLINE_STEP;
|
||||||
|
property XErrorBars;
|
||||||
|
property YErrorBars;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TFitParamsState = (fpsUnknown, fpsInvalid, fpsValid);
|
TFitParamsState = (fpsUnknown, fpsInvalid, fpsValid);
|
||||||
@ -338,8 +342,10 @@ type
|
|||||||
property Pen: TChartPen read FPen write SetPen;
|
property Pen: TChartPen read FPen write SetPen;
|
||||||
property Pointer;
|
property Pointer;
|
||||||
property Source;
|
property Source;
|
||||||
property ToolTargets default [nptPoint, nptCustom];
|
|
||||||
property Step: TFuncSeriesStep read FStep write SetStep default DEF_FIT_STEP;
|
property Step: TFuncSeriesStep read FStep write SetStep default DEF_FIT_STEP;
|
||||||
|
property ToolTargets default [nptPoint, nptCustom];
|
||||||
|
property XErrorBars;
|
||||||
|
property YErrorBars;
|
||||||
property OnCalcGoodnessOfFit: TCalcGoodnessOfFitEvent
|
property OnCalcGoodnessOfFit: TCalcGoodnessOfFitEvent
|
||||||
read FOnCalcGoodnessOfFit write FOnCalcGoodnessOfFit;
|
read FOnCalcGoodnessOfFit write FOnCalcGoodnessOfFit;
|
||||||
property OnCustomDrawPointer;
|
property OnCustomDrawPointer;
|
||||||
@ -1041,6 +1047,7 @@ begin
|
|||||||
for startIndex := splineStart to splineEnd + Degree - 1 do
|
for startIndex := splineStart to splineEnd + Degree - 1 do
|
||||||
SplineSegment(0.0, 1.0, SplinePoint(0.0), SplinePoint(1.0));
|
SplineSegment(0.0, 1.0, SplinePoint(0.0), SplinePoint(1.0));
|
||||||
end;
|
end;
|
||||||
|
DrawErrorBars(ADrawer);
|
||||||
DrawLabels(ADrawer);
|
DrawLabels(ADrawer);
|
||||||
DrawPointers(ADrawer);
|
DrawPointers(ADrawer);
|
||||||
end;
|
end;
|
||||||
@ -1290,6 +1297,7 @@ begin
|
|||||||
if not s.IsFewPoints then
|
if not s.IsFewPoints then
|
||||||
DrawSpline(s);
|
DrawSpline(s);
|
||||||
|
|
||||||
|
DrawErrorBars(ADrawer);
|
||||||
DrawLabels(ADrawer);
|
DrawLabels(ADrawer);
|
||||||
DrawPointers(ADrawer);
|
DrawPointers(ADrawer);
|
||||||
end;
|
end;
|
||||||
@ -1570,6 +1578,7 @@ begin
|
|||||||
Free;
|
Free;
|
||||||
end;
|
end;
|
||||||
DrawLabels(ADrawer);
|
DrawLabels(ADrawer);
|
||||||
|
DrawErrorBars(ADrawer);
|
||||||
DrawPointers(ADrawer);
|
DrawPointers(ADrawer);
|
||||||
finally
|
finally
|
||||||
de.Free;
|
de.Free;
|
||||||
|
@ -243,6 +243,8 @@ type
|
|||||||
property Styles;
|
property Styles;
|
||||||
property ToolTargets;
|
property ToolTargets;
|
||||||
property UseReticule default true;
|
property UseReticule default true;
|
||||||
|
property XErrorBars;
|
||||||
|
property YErrorBars;
|
||||||
// Events
|
// Events
|
||||||
property OnDrawPointer: TSeriesPointerDrawEvent
|
property OnDrawPointer: TSeriesPointerDrawEvent
|
||||||
read FOnDrawPointer write FOnDrawPointer; deprecated 'Use OnCustomDrawPointer';
|
read FOnDrawPointer write FOnDrawPointer; deprecated 'Use OnCustomDrawPointer';
|
||||||
@ -426,6 +428,7 @@ begin
|
|||||||
PrepareGraphPoints(ext, LineType <> ltFromOrigin);
|
PrepareGraphPoints(ext, LineType <> ltFromOrigin);
|
||||||
DrawSingleLineInStack(ADrawer, 0);
|
DrawSingleLineInStack(ADrawer, 0);
|
||||||
for i := 0 to Source.YCount - 2 do begin
|
for i := 0 to Source.YCount - 2 do begin
|
||||||
|
if Source.IsYErrorIndex(i+1) then Continue;
|
||||||
UpdateGraphPoints(i, FStacked);
|
UpdateGraphPoints(i, FStacked);
|
||||||
DrawSingleLineInStack(ADrawer, i + 1);
|
DrawSingleLineInStack(ADrawer, i + 1);
|
||||||
end;
|
end;
|
||||||
@ -663,6 +666,7 @@ begin
|
|||||||
else
|
else
|
||||||
DrawColoredLines;
|
DrawColoredLines;
|
||||||
end;
|
end;
|
||||||
|
DrawErrorBars(ADrawer);
|
||||||
DrawLabels(ADrawer);
|
DrawLabels(ADrawer);
|
||||||
if ShowPoints then
|
if ShowPoints then
|
||||||
DrawPointers(ADrawer, AIndex, FColorEach in [cePoint, cePointAndLineBefore, cePointAndLineAfter]);
|
DrawPointers(ADrawer, AIndex, FColorEach in [cePoint, cePointAndLineBefore, cePointAndLineAfter]);
|
||||||
|
@ -68,6 +68,8 @@ type
|
|||||||
published
|
published
|
||||||
property DataPoints: TStrings read FDataPoints write SetDataPoints;
|
property DataPoints: TStrings read FDataPoints write SetDataPoints;
|
||||||
property Sorted: Boolean read FSorted write SetSorted default false;
|
property Sorted: Boolean read FSorted write SetSorted default false;
|
||||||
|
property XErrorBarData;
|
||||||
|
property YErrorBarData;
|
||||||
property YCount;
|
property YCount;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -135,6 +137,8 @@ type
|
|||||||
property YMax: Double read FYMax write SetYMax;
|
property YMax: Double read FYMax write SetYMax;
|
||||||
property YMin: Double read FYMin write SetYMin;
|
property YMin: Double read FYMin write SetYMin;
|
||||||
property YNanPercent: TPercent read FYNanPercent write SetYNanPercent default 0;
|
property YNanPercent: TPercent read FYNanPercent write SetYNanPercent default 0;
|
||||||
|
property XErrorBarData;
|
||||||
|
property YErrorBarData;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
TUserDefinedChartSource = class;
|
TUserDefinedChartSource = class;
|
||||||
|
@ -268,6 +268,22 @@ type
|
|||||||
property Visible default false;
|
property Visible default false;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
TChartErrorBar = class(TChartElement)
|
||||||
|
private
|
||||||
|
FWidth: Integer;
|
||||||
|
FPen: TPen;
|
||||||
|
procedure SetPen(const AValue: TPen);
|
||||||
|
procedure SetWidth(const AValue: Integer);
|
||||||
|
public
|
||||||
|
constructor Create(AOwner: TCustomChart);
|
||||||
|
destructor Destroy; override;
|
||||||
|
procedure Assign(ASource: TPersistent); override;
|
||||||
|
published
|
||||||
|
property Pen: TPen read FPen write SetPen;
|
||||||
|
property Visible default false;
|
||||||
|
property Width: Integer read FWidth write SetWidth default -1;
|
||||||
|
end;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
@ -823,5 +839,44 @@ begin
|
|||||||
StyleChanged(Self);
|
StyleChanged(Self);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TChartErrorBar }
|
||||||
|
|
||||||
|
constructor TChartErrorBar.Create(AOwner: TCustomChart);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
FWidth := -1; // -1 = same width as series pointer
|
||||||
|
FPen := TPen.Create;
|
||||||
|
FPen.OnChange := @StyleChanged;
|
||||||
|
FVisible := false;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TChartErrorBar.Destroy;
|
||||||
|
begin
|
||||||
|
FPen.Free;
|
||||||
|
inherited Destroy;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TChartErrorBar.Assign(ASource: TPersistent);
|
||||||
|
begin
|
||||||
|
if ASource is TChartErrorBar then begin
|
||||||
|
FPen.Assign(TChartErrorBar(ASource).Pen);
|
||||||
|
FWidth := TChartErrorBar(ASource).Width;
|
||||||
|
end;
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TChartErrorBar.SetPen(const AValue: TPen);
|
||||||
|
begin
|
||||||
|
FPen.Assign(AValue);
|
||||||
|
StyleChanged(Self);
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TChartErrorBar.SetWidth(const AValue: Integer);
|
||||||
|
begin
|
||||||
|
if FWidth = AValue then exit;
|
||||||
|
FWidth := AValue;
|
||||||
|
StyleChanged(self);
|
||||||
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user