TAChart: Initial implementation of error bars for TLineSeries, TCubicSplineSeries, TBSplineSeries and TFitSeries.

git-svn-id: trunk@58597 -
This commit is contained in:
wp 2018-07-22 22:03:36 +00:00
parent 12955c9ea1
commit 1168572c7d
6 changed files with 469 additions and 15 deletions

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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]);

View File

@ -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;

View File

@ -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.