mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-29 19:52:26 +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
|
||||
DEF_AXIS_INDEX = -1;
|
||||
DEF_ERR_ENDLENGTH = 5;
|
||||
|
||||
type
|
||||
TNearestPointParams = record
|
||||
@ -257,9 +258,12 @@ type
|
||||
TBasicPointSeries = class(TChartSeries)
|
||||
strict private
|
||||
FMarkPositions: TLinearMarkPositions;
|
||||
FErrorBars: array[0..1] of TChartErrorBar;
|
||||
FOnCustomDrawPointer: TSeriesPointerCustomDrawEvent;
|
||||
FOnGetPointerStyle: TSeriesPointerStyleEvent;
|
||||
function GetErrorBars(AIndex: Integer): TChartErrorBar;
|
||||
function GetLabelDirection(AIndex: Integer): TLabelDirection;
|
||||
procedure SetErrorBars(AIndex: Integer; AValue: TChartErrorBar);
|
||||
procedure SetMarkPositions(AValue: TLinearMarkPositions);
|
||||
procedure SetPointer(AValue: TSeriesPointer);
|
||||
procedure SetStacked(AValue: Boolean);
|
||||
@ -276,11 +280,16 @@ type
|
||||
|
||||
procedure AfterDrawPointer(
|
||||
ADrawer: IChartDrawer; AIndex: Integer; const APos: TPoint); virtual;
|
||||
procedure DrawErrorBars(ADrawer: IChartDrawer);
|
||||
procedure DrawLabels(ADrawer: IChartDrawer);
|
||||
procedure DrawPointers(ADrawer: IChartDrawer; AStyleIndex: Integer = 0;
|
||||
UseDataColors: Boolean = false);
|
||||
procedure FindExtentInterval(
|
||||
const AExtent: TDoubleRect; AFilterByExtent: Boolean);
|
||||
{
|
||||
function GetErrorBars(APointIndex: Integer; IsXError: Boolean;
|
||||
out AGraphPointPos, AGraphPointNeg: Double): Boolean;
|
||||
}
|
||||
function GetLabelDataPoint(AIndex: Integer): TDoublePoint; virtual;
|
||||
procedure GetLegendItemsRect(AItems: TChartLegendItems; ABrush: TBrush);
|
||||
function GetXRange(AX: Double; AIndex: Integer): Double;
|
||||
@ -296,9 +305,13 @@ type
|
||||
|
||||
property Pointer: TSeriesPointer read FPointer write SetPointer;
|
||||
property Stacked: Boolean read FStacked write SetStacked;
|
||||
|
||||
protected
|
||||
procedure AfterAdd; 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
|
||||
read FOnCustomDrawPointer write FOnCustomDrawPointer;
|
||||
property OnGetPointerStyle: TSeriesPointerStyleEvent
|
||||
@ -1076,10 +1089,15 @@ end;
|
||||
{ TBasicPointSeries }
|
||||
|
||||
procedure TBasicPointSeries.AfterAdd;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
inherited AfterAdd;
|
||||
if Pointer <> nil then
|
||||
Pointer.SetOwner(ParentChart);
|
||||
for i := 0 to 1 do
|
||||
if FErrorBars[i] <> nil then
|
||||
FErrorBars[i].SetOwner(ParentChart);
|
||||
end;
|
||||
|
||||
procedure TBasicPointSeries.AfterDrawPointer(
|
||||
@ -1105,16 +1123,96 @@ end;
|
||||
constructor TBasicPointSeries.Create(AOwner: TComponent);
|
||||
begin
|
||||
inherited;
|
||||
FErrorBars[0] := TChartErrorBar.Create(FChart);
|
||||
FErrorBars[1] := TChartErrorBar.Create(FChart);
|
||||
FOptimizeX := true;
|
||||
ToolTargets := [nptPoint, nptYList];
|
||||
end;
|
||||
|
||||
destructor TBasicPointSeries.Destroy;
|
||||
begin
|
||||
FreeAndNil(FErrorBars[0]);
|
||||
FreeAndNil(FErrorBars[1]);
|
||||
FreeAndNil(FPointer);
|
||||
inherited;
|
||||
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);
|
||||
var
|
||||
prevLabelPoly: TPointArray;
|
||||
@ -1272,6 +1370,11 @@ begin
|
||||
end;
|
||||
end;
|
||||
|
||||
function TBasicPointSeries.GetErrorBars(AIndex: Integer): TChartErrorBar;
|
||||
begin
|
||||
Result := FErrorBars[AIndex];
|
||||
end;
|
||||
|
||||
function TBasicPointSeries.GetLabelDataPoint(AIndex: Integer): TDoublePoint;
|
||||
begin
|
||||
Result := GetGraphPoint(AIndex);
|
||||
@ -1518,6 +1621,13 @@ begin
|
||||
FGraphPoints[i - FLoBound] := GetGraphPoint(i);
|
||||
end;
|
||||
|
||||
procedure TBasicPointSeries.SetErrorBars(AIndex: Integer;
|
||||
AValue: TChartErrorBar);
|
||||
begin
|
||||
FErrorBars[AIndex] := AValue;
|
||||
UpdateParentChart;
|
||||
end;
|
||||
|
||||
procedure TBasicPointSeries.SetMarkPositions(AValue: TLinearMarkPositions);
|
||||
begin
|
||||
if FMarkPositions = AValue then exit;
|
||||
|
@ -149,8 +149,32 @@ type
|
||||
property Current: PChartDataItem read GetCurrent;
|
||||
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)
|
||||
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(
|
||||
var AValues: TChartValueTextArray; AStart, AEnd: Integer);
|
||||
strict protected
|
||||
@ -160,14 +184,21 @@ type
|
||||
FValuesTotalIsValid: Boolean;
|
||||
FXCount: Cardinal;
|
||||
FYCount: Cardinal;
|
||||
|
||||
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;
|
||||
procedure InvalidateCaches;
|
||||
procedure SetXCount(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
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
public
|
||||
procedure AfterDraw; virtual;
|
||||
procedure BeforeDraw; virtual;
|
||||
@ -183,6 +214,14 @@ type
|
||||
function FormatItemXYText(
|
||||
const AFormat: String; AX, AY: Double; AText: String): String;
|
||||
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;
|
||||
procedure ValuesInRange(
|
||||
AParams: TValuesInRangeParams; var AValues: TChartValueTextArray); virtual;
|
||||
@ -474,7 +513,8 @@ begin
|
||||
YList[AIndex - 1] := AValue;
|
||||
end;
|
||||
|
||||
{ TBaisChartSource }
|
||||
|
||||
{ TBasicChartSource }
|
||||
|
||||
constructor TBasicChartSource.Create(AOwner: TComponent);
|
||||
begin
|
||||
@ -647,6 +687,45 @@ begin
|
||||
FIndex := 0;
|
||||
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 }
|
||||
|
||||
procedure TCustomChartSource.AfterDraw;
|
||||
@ -665,10 +744,29 @@ begin
|
||||
end;
|
||||
|
||||
constructor TCustomChartSource.Create(AOwner: TComponent);
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
inherited Create(AOwner);
|
||||
FXCount := 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;
|
||||
|
||||
procedure TCustomChartSource.EndUpdate;
|
||||
@ -683,46 +781,97 @@ end;
|
||||
function TCustomChartSource.Extent: TDoubleRect;
|
||||
var
|
||||
i: Integer;
|
||||
vhi, vlo: Double;
|
||||
begin
|
||||
if FExtentIsValid then exit(FExtent);
|
||||
FExtent := EmptyExtent;
|
||||
for i := 0 to Count - 1 do
|
||||
with Item[i]^ do begin
|
||||
UpdateMinMax(X, FExtent.a.X, FExtent.b.X);
|
||||
UpdateMinMax(Y, FExtent.a.Y, FExtent.b.Y);
|
||||
end;
|
||||
|
||||
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
|
||||
UpdateMinMax(Item[i]^.X, FExtent.a.X, FExtent.b.X);
|
||||
|
||||
if HasYErrorBars then
|
||||
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;
|
||||
Result := FExtent;
|
||||
end;
|
||||
|
||||
{ Calculates the extent of multiple y values stacked onto each other. }
|
||||
function TCustomChartSource.ExtentCumulative: TDoubleRect;
|
||||
var
|
||||
h: Double;
|
||||
i, j: Integer;
|
||||
jyp: Integer = -1;
|
||||
jyn: Integer = -1;
|
||||
begin
|
||||
Result := Extent;
|
||||
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
|
||||
h := NumberOr(Item[i]^.Y);
|
||||
for j := 0 to YCount - 2 do begin
|
||||
h += NumberOr(Item[i]^.YList[j]);
|
||||
// If some of Y values are negative, h may be non-monotonic.
|
||||
UpdateMinMax(h, Result.a.Y, Result.b.Y);
|
||||
end;
|
||||
for j := 0 to YCount - 2 do
|
||||
if (j <> jyp) and (j <> jyn) then begin
|
||||
h += NumberOr(Item[i]^.YList[j]);
|
||||
// If some of the Y values are negative, h may be non-monotonic.
|
||||
UpdateMinMax(h, Result.a.Y, Result.b.Y);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ Calculates the extent including multiple x and y values (non-stacked) }
|
||||
function TCustomChartSource.ExtentList: TDoubleRect;
|
||||
var
|
||||
i, j: Integer;
|
||||
jxp, jxn: Integer;
|
||||
jyp, jyn: Integer;
|
||||
begin
|
||||
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
|
||||
with Item[i]^ do begin
|
||||
for j := 0 to High(XList) do
|
||||
UpdateMinMax(XList[j], Result.a.X, Result.b.X);
|
||||
if (j <> jxp) and (j <> jxn) then
|
||||
UpdateMinMax(XList[j], Result.a.X, Result.b.X);
|
||||
for j := 0 to High(YList) do
|
||||
UpdateMinMax(YList[j], Result.a.Y, Result.b.Y);
|
||||
if (j <> jyp) and (j <> jyn) then
|
||||
UpdateMinMax(YList[j], Result.a.Y, Result.b.Y);
|
||||
end;
|
||||
end;
|
||||
|
||||
@ -805,17 +954,140 @@ begin
|
||||
Result := TCustomChartSourceEnumerator.Create(Self);
|
||||
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;
|
||||
begin
|
||||
FExtentIsValid := false;
|
||||
FValuesTotalIsValid := false;
|
||||
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;
|
||||
begin
|
||||
Result := false;
|
||||
end;
|
||||
|
||||
procedure TCustomChartSource.SetErrorbarData(AIndex: Integer;
|
||||
AValue: TChartErrorBarData);
|
||||
begin
|
||||
FErrorbarData[AIndex] := AValue;
|
||||
Notify;
|
||||
end;
|
||||
|
||||
procedure TCustomChartSource.SortValuesInRange(
|
||||
var AValues: TChartValueTextArray; AStart, AEnd: Integer);
|
||||
var
|
||||
|
@ -183,6 +183,8 @@ type
|
||||
property Step: TFuncSeriesStep
|
||||
read FStep write SetStep default DEF_SPLINE_STEP;
|
||||
property ToolTargets default [nptPoint, nptCustom];
|
||||
property XErrorBars;
|
||||
property YErrorBars;
|
||||
property OnCustomDrawPointer;
|
||||
property OnGetPointerStyle;
|
||||
end;
|
||||
@ -271,6 +273,8 @@ type
|
||||
read FSplineType write SetSplineType default cstNatural;
|
||||
property Step: TFuncSeriesStep
|
||||
read FStep write SetStep default DEF_SPLINE_STEP;
|
||||
property XErrorBars;
|
||||
property YErrorBars;
|
||||
end;
|
||||
|
||||
TFitParamsState = (fpsUnknown, fpsInvalid, fpsValid);
|
||||
@ -338,8 +342,10 @@ type
|
||||
property Pen: TChartPen read FPen write SetPen;
|
||||
property Pointer;
|
||||
property Source;
|
||||
property ToolTargets default [nptPoint, nptCustom];
|
||||
property Step: TFuncSeriesStep read FStep write SetStep default DEF_FIT_STEP;
|
||||
property ToolTargets default [nptPoint, nptCustom];
|
||||
property XErrorBars;
|
||||
property YErrorBars;
|
||||
property OnCalcGoodnessOfFit: TCalcGoodnessOfFitEvent
|
||||
read FOnCalcGoodnessOfFit write FOnCalcGoodnessOfFit;
|
||||
property OnCustomDrawPointer;
|
||||
@ -1041,6 +1047,7 @@ begin
|
||||
for startIndex := splineStart to splineEnd + Degree - 1 do
|
||||
SplineSegment(0.0, 1.0, SplinePoint(0.0), SplinePoint(1.0));
|
||||
end;
|
||||
DrawErrorBars(ADrawer);
|
||||
DrawLabels(ADrawer);
|
||||
DrawPointers(ADrawer);
|
||||
end;
|
||||
@ -1290,6 +1297,7 @@ begin
|
||||
if not s.IsFewPoints then
|
||||
DrawSpline(s);
|
||||
|
||||
DrawErrorBars(ADrawer);
|
||||
DrawLabels(ADrawer);
|
||||
DrawPointers(ADrawer);
|
||||
end;
|
||||
@ -1570,6 +1578,7 @@ begin
|
||||
Free;
|
||||
end;
|
||||
DrawLabels(ADrawer);
|
||||
DrawErrorBars(ADrawer);
|
||||
DrawPointers(ADrawer);
|
||||
finally
|
||||
de.Free;
|
||||
|
@ -243,6 +243,8 @@ type
|
||||
property Styles;
|
||||
property ToolTargets;
|
||||
property UseReticule default true;
|
||||
property XErrorBars;
|
||||
property YErrorBars;
|
||||
// Events
|
||||
property OnDrawPointer: TSeriesPointerDrawEvent
|
||||
read FOnDrawPointer write FOnDrawPointer; deprecated 'Use OnCustomDrawPointer';
|
||||
@ -426,6 +428,7 @@ begin
|
||||
PrepareGraphPoints(ext, LineType <> ltFromOrigin);
|
||||
DrawSingleLineInStack(ADrawer, 0);
|
||||
for i := 0 to Source.YCount - 2 do begin
|
||||
if Source.IsYErrorIndex(i+1) then Continue;
|
||||
UpdateGraphPoints(i, FStacked);
|
||||
DrawSingleLineInStack(ADrawer, i + 1);
|
||||
end;
|
||||
@ -663,6 +666,7 @@ begin
|
||||
else
|
||||
DrawColoredLines;
|
||||
end;
|
||||
DrawErrorBars(ADrawer);
|
||||
DrawLabels(ADrawer);
|
||||
if ShowPoints then
|
||||
DrawPointers(ADrawer, AIndex, FColorEach in [cePoint, cePointAndLineBefore, cePointAndLineAfter]);
|
||||
|
@ -68,6 +68,8 @@ type
|
||||
published
|
||||
property DataPoints: TStrings read FDataPoints write SetDataPoints;
|
||||
property Sorted: Boolean read FSorted write SetSorted default false;
|
||||
property XErrorBarData;
|
||||
property YErrorBarData;
|
||||
property YCount;
|
||||
end;
|
||||
|
||||
@ -135,6 +137,8 @@ type
|
||||
property YMax: Double read FYMax write SetYMax;
|
||||
property YMin: Double read FYMin write SetYMin;
|
||||
property YNanPercent: TPercent read FYNanPercent write SetYNanPercent default 0;
|
||||
property XErrorBarData;
|
||||
property YErrorBarData;
|
||||
end;
|
||||
|
||||
TUserDefinedChartSource = class;
|
||||
|
@ -268,6 +268,22 @@ type
|
||||
property Visible default false;
|
||||
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
|
||||
|
||||
uses
|
||||
@ -823,5 +839,44 @@ begin
|
||||
StyleChanged(Self);
|
||||
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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user