mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-31 21:52:48 +02:00
TAChart: Activate painting of box/whisker and OHLC series marks. Fix missing value issues in these series.
git-svn-id: trunk@60445 -
This commit is contained in:
parent
6cb99018d7
commit
cf88b3536a
@ -302,7 +302,7 @@ type
|
|||||||
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;
|
||||||
function GetZeroLevel: Double; virtual;
|
function GetZeroLevel: Double; virtual;
|
||||||
function HasMissingYValue(AIndex: Integer): Boolean;
|
function HasMissingYValue(AIndex: Integer; AMaxYIndex: Integer = MaxInt): Boolean;
|
||||||
function NearestXNumber(var AIndex: Integer; ADir: Integer): Double;
|
function NearestXNumber(var AIndex: Integer; ADir: Integer): Double;
|
||||||
procedure PrepareGraphPoints(
|
procedure PrepareGraphPoints(
|
||||||
const AExtent: TDoubleRect; AFilterByExtent: Boolean);
|
const AExtent: TDoubleRect; AFilterByExtent: Boolean);
|
||||||
@ -1655,13 +1655,14 @@ end;
|
|||||||
|
|
||||||
{ Returns true if the data point at the given index has at least one missing
|
{ Returns true if the data point at the given index has at least one missing
|
||||||
y value (NaN) }
|
y value (NaN) }
|
||||||
function TBasicPointSeries.HasMissingYValue(AIndex: Integer): Boolean;
|
function TBasicPointSeries.HasMissingYValue(AIndex: Integer;
|
||||||
|
AMaxYIndex: Integer = MaxInt): Boolean;
|
||||||
var
|
var
|
||||||
j: Integer;
|
j: Integer;
|
||||||
begin
|
begin
|
||||||
Result := IsNaN(Source[AIndex]^.Y);
|
Result := IsNaN(Source[AIndex]^.Y);
|
||||||
if not Result then
|
if not Result then
|
||||||
for j := 0 to Source.YCount-1 do
|
for j := 0 to Min(AMaxYIndex, Source.YCount)-2 do
|
||||||
if IsNaN(Source[AIndex]^.YList[j]) then
|
if IsNaN(Source[AIndex]^.YList[j]) then
|
||||||
exit(true);
|
exit(true);
|
||||||
end;
|
end;
|
||||||
@ -1959,7 +1960,7 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
{ Can be overridden for a data-point dependent reference level, such as in
|
{ Can be overridden for a data-point dependent reference level, such as in
|
||||||
TBubbleSeries. AIndex is relative to FLoBound }
|
TBubbleSeries. AIndex refers to chart source. }
|
||||||
procedure TBasicPointSeries.UpdateLabelDirectionReferenceLevel(AIndex, AYIndex: Integer;
|
procedure TBasicPointSeries.UpdateLabelDirectionReferenceLevel(AIndex, AYIndex: Integer;
|
||||||
var ALevel: Double);
|
var ALevel: Double);
|
||||||
begin
|
begin
|
||||||
|
@ -112,8 +112,11 @@ type
|
|||||||
procedure GetLegendItems(AItems: TChartLegendItems); override;
|
procedure GetLegendItems(AItems: TChartLegendItems); override;
|
||||||
function GetSeriesColor: TColor; override;
|
function GetSeriesColor: TColor; override;
|
||||||
class procedure GetXYCountNeeded(out AXCount, AYCount: Integer); override;
|
class procedure GetXYCountNeeded(out AXCount, AYCount: Integer); override;
|
||||||
|
function SkipMissingValues(AIndex: Integer): Boolean; override;
|
||||||
function ToolTargetDistance(const AParams: TNearestPointParams;
|
function ToolTargetDistance(const AParams: TNearestPointParams;
|
||||||
AGraphPt: TDoublePoint; APointIdx, AXIdx, AYIdx: Integer): Integer; override;
|
AGraphPt: TDoublePoint; APointIdx, AXIdx, AYIdx: Integer): Integer; override;
|
||||||
|
procedure UpdateLabelDirectionReferenceLevel(AIndex, AYIndex: Integer;
|
||||||
|
var ALevel: Double); override;
|
||||||
public
|
public
|
||||||
function AddXY(
|
function AddXY(
|
||||||
AX, AYLoWhisker, AYLoBox, AY, AYHiBox, AYHiWhisker: Double;
|
AX, AYLoWhisker, AYLoBox, AY, AYHiBox, AYHiWhisker: Double;
|
||||||
@ -142,6 +145,8 @@ type
|
|||||||
published
|
published
|
||||||
property AxisIndexX;
|
property AxisIndexX;
|
||||||
property AxisIndexY;
|
property AxisIndexY;
|
||||||
|
property MarkPositions;
|
||||||
|
property Marks;
|
||||||
property Source;
|
property Source;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -180,8 +185,11 @@ type
|
|||||||
procedure GetLegendItems(AItems: TChartLegendItems); override;
|
procedure GetLegendItems(AItems: TChartLegendItems); override;
|
||||||
function GetSeriesColor: TColor; override;
|
function GetSeriesColor: TColor; override;
|
||||||
class procedure GetXYCountNeeded(out AXCount, AYCount: Integer); override;
|
class procedure GetXYCountNeeded(out AXCount, AYCount: Integer); override;
|
||||||
|
function SkipMissingValues(AIndex: Integer): Boolean; override;
|
||||||
function ToolTargetDistance(const AParams: TNearestPointParams;
|
function ToolTargetDistance(const AParams: TNearestPointParams;
|
||||||
AGraphPt: TDoublePoint; APointIdx, AXIdx, AYIdx: Integer): Integer; override;
|
AGraphPt: TDoublePoint; APointIdx, AXIdx, AYIdx: Integer): Integer; override;
|
||||||
|
procedure UpdateLabelDirectionReferenceLevel(AIndex, AYIndex: Integer;
|
||||||
|
var ALevel: Double); override;
|
||||||
public
|
public
|
||||||
procedure Assign(ASource: TPersistent); override;
|
procedure Assign(ASource: TPersistent); override;
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
@ -217,6 +225,8 @@ type
|
|||||||
published
|
published
|
||||||
property AxisIndexX;
|
property AxisIndexX;
|
||||||
property AxisIndexY;
|
property AxisIndexY;
|
||||||
|
property MarkPositions;
|
||||||
|
property Marks;
|
||||||
property Source;
|
property Source;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -505,8 +515,10 @@ var
|
|||||||
irect: TRect;
|
irect: TRect;
|
||||||
dummyR: TRect = (Left:0; Top:0; Right:0; Bottom:0);
|
dummyR: TRect = (Left:0; Top:0; Right:0; Bottom:0);
|
||||||
ext: TDoubleRect;
|
ext: TDoubleRect;
|
||||||
|
nx, ny: Integer;
|
||||||
begin
|
begin
|
||||||
if Source.YCount < 2 then exit;
|
GetXYCountNeeded(nx, ny);
|
||||||
|
if Source.YCount < ny then exit;
|
||||||
|
|
||||||
ADrawer.Pen := BubblePen;
|
ADrawer.Pen := BubblePen;
|
||||||
ADrawer.Brush := BubbleBrush;
|
ADrawer.Brush := BubbleBrush;
|
||||||
@ -529,8 +541,10 @@ begin
|
|||||||
ADrawer.SetBrushColor(ColorDef(item^.Color, BubbleBrush.Color));
|
ADrawer.SetBrushColor(ColorDef(item^.Color, BubbleBrush.Color));
|
||||||
ADrawer.Ellipse(irect.Left, irect.Top, irect.Right, irect.Bottom);
|
ADrawer.Ellipse(irect.Left, irect.Top, irect.Right, irect.Bottom);
|
||||||
end;
|
end;
|
||||||
for i := 0 to Min(1, Source.YCount) do
|
if Source.YCount > ny then
|
||||||
DrawLabels(ADrawer, i);
|
for i := 0 to ny - 1 do DrawLabels(ADrawer, i)
|
||||||
|
else
|
||||||
|
DrawLabels(ADrawer);
|
||||||
ADrawer.ClippingStop;
|
ADrawer.ClippingStop;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -970,8 +984,10 @@ var
|
|||||||
ext2: TDoubleRect;
|
ext2: TDoubleRect;
|
||||||
x, ymin, yqmin, ymed, yqmax, ymax, wb, ww, w: Double;
|
x, ymin, yqmin, ymed, yqmax, ymax, wb, ww, w: Double;
|
||||||
i: Integer;
|
i: Integer;
|
||||||
|
nx, ny: Integer;
|
||||||
begin
|
begin
|
||||||
if IsEmpty or (Source.YCount < 5) then
|
GetXYCountNeeded(nx, ny);
|
||||||
|
if IsEmpty or (Source.YCount < ny) then
|
||||||
exit;
|
exit;
|
||||||
if FWidthStyle = bwsPercentMin then
|
if FWidthStyle = bwsPercentMin then
|
||||||
UpdateMinXRange;
|
UpdateMinXRange;
|
||||||
@ -1016,11 +1032,17 @@ begin
|
|||||||
ADrawer.SetBrushParams(bsClear, clTAColor);
|
ADrawer.SetBrushParams(bsClear, clTAColor);
|
||||||
DoLine(x - wb, ymed, x + wb, ymed);
|
DoLine(x - wb, ymed, x + wb, ymed);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if Source.YCount > ny then
|
||||||
|
for i := 0 to ny-1 do DrawLabels(ADrawer, ny)
|
||||||
|
else
|
||||||
|
DrawLabels(ADrawer);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TBoxAndWhiskerSeries.Extent: TDoubleRect;
|
function TBoxAndWhiskerSeries.Extent: TDoubleRect;
|
||||||
var
|
var
|
||||||
x: Double;
|
x: Double;
|
||||||
|
j: Integer;
|
||||||
|
|
||||||
function ExtraWidth(AIndex: Integer): Double;
|
function ExtraWidth(AIndex: Integer): Double;
|
||||||
begin
|
begin
|
||||||
@ -1031,10 +1053,20 @@ begin
|
|||||||
if Source.YCount < 5 then exit(EmptyExtent);
|
if Source.YCount < 5 then exit(EmptyExtent);
|
||||||
Result := Source.ExtentList;
|
Result := Source.ExtentList;
|
||||||
// Show first and last boxes fully.
|
// Show first and last boxes fully.
|
||||||
x := GetGraphPointX(0);
|
j := -1;
|
||||||
Result.a.X := Min(Result.a.X, GraphToAxisX(x - ExtraWidth(0)));
|
x := NaN;
|
||||||
x := GetGraphPointX(Count - 1);
|
while IsNaN(x) and (j < Source.Count-1) do begin
|
||||||
Result.b.X := Max(Result.b.X, GraphToAxisX(x + ExtraWidth(Count - 1)));
|
inc(j);
|
||||||
|
x := GetGraphPointX(j);
|
||||||
|
end;
|
||||||
|
Result.a.X := Min(Result.a.X, GraphToAxisX(x - ExtraWidth(j)));
|
||||||
|
j := Count;
|
||||||
|
x := NaN;
|
||||||
|
while IsNaN(x) and (j > 0) do begin
|
||||||
|
dec(j);
|
||||||
|
x := GetGraphPointX(j);
|
||||||
|
end;
|
||||||
|
Result.b.X := Max(Result.b.X, GraphToAxisX(x + ExtraWidth(j)));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TBoxAndWhiskerSeries.GetLegendItems(AItems: TChartLegendItems);
|
procedure TBoxAndWhiskerSeries.GetLegendItems(AItems: TChartLegendItems);
|
||||||
@ -1178,6 +1210,13 @@ begin
|
|||||||
UpdateParentChart;
|
UpdateParentChart;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TBoxAndWhiskerSeries.SkipMissingValues(AIndex: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := IsNaN(Source[AIndex]^.Point);
|
||||||
|
if not Result then
|
||||||
|
Result := HasMissingYValue(AIndex, 5);
|
||||||
|
end;
|
||||||
|
|
||||||
function TBoxAndWhiskerSeries.ToolTargetDistance(
|
function TBoxAndWhiskerSeries.ToolTargetDistance(
|
||||||
const AParams: TNearestPointParams; AGraphPt: TDoublePoint;
|
const AParams: TNearestPointParams; AGraphPt: TDoublePoint;
|
||||||
APointIdx, AXIdx, AYIdx: Integer): Integer;
|
APointIdx, AXIdx, AYIdx: Integer): Integer;
|
||||||
@ -1231,6 +1270,20 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TBoxAndWhiskerSeries.UpdateLabelDirectionReferenceLevel(
|
||||||
|
AIndex, AYIndex: Integer; var ALevel: Double);
|
||||||
|
var
|
||||||
|
item: PChartDataItem;
|
||||||
|
begin
|
||||||
|
case AYIndex of
|
||||||
|
0: ALevel := +Infinity;
|
||||||
|
3: ALevel := -Infinity;
|
||||||
|
else
|
||||||
|
item := Source.Item[AIndex];
|
||||||
|
ALevel := (AxisToGraphY(item^.GetY(0)) + AxisToGraphY(item^.GetY(3)))*0.5;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TOpenHighLowCloseSeries }
|
{ TOpenHighLowCloseSeries }
|
||||||
|
|
||||||
@ -1382,6 +1435,7 @@ var
|
|||||||
i: Integer;
|
i: Integer;
|
||||||
x, tw, yopen, yhigh, ylow, yclose: Double;
|
x, tw, yopen, yhigh, ylow, yclose: Double;
|
||||||
p: TPen;
|
p: TPen;
|
||||||
|
nx, ny: Integer;
|
||||||
begin
|
begin
|
||||||
my := MaxIntValue([YIndexOpen, YIndexHigh, YIndexLow, YIndexClose]);
|
my := MaxIntValue([YIndexOpen, YIndexHigh, YIndexLow, YIndexClose]);
|
||||||
if IsEmpty or (my >= Source.YCount) then exit;
|
if IsEmpty or (my >= Source.YCount) then exit;
|
||||||
@ -1394,12 +1448,16 @@ begin
|
|||||||
|
|
||||||
for i := FLoBound to FUpBound do begin
|
for i := FLoBound to FUpBound do begin
|
||||||
x := GetGraphPointX(i);
|
x := GetGraphPointX(i);
|
||||||
|
if IsNaN(x) then Continue;
|
||||||
yopen := GetGraphPointY(i, YIndexOpen);
|
yopen := GetGraphPointY(i, YIndexOpen);
|
||||||
|
if IsNaN(yopen) then Continue;
|
||||||
yhigh := GetGraphPointY(i, YIndexHigh);
|
yhigh := GetGraphPointY(i, YIndexHigh);
|
||||||
|
if IsNaN(yhigh) then Continue;
|
||||||
ylow := GetGraphPointY(i, YIndexLow);
|
ylow := GetGraphPointY(i, YIndexLow);
|
||||||
|
if IsNaN(ylow) then Continue;
|
||||||
yclose := GetGraphPointY(i, YIndexClose);
|
yclose := GetGraphPointY(i, YIndexClose);
|
||||||
|
if IsNaN(yclose) then Continue;
|
||||||
tw := GetXRange(x, i) * PERCENT * TickWidth;
|
tw := GetXRange(x, i) * PERCENT * TickWidth;
|
||||||
|
|
||||||
if (yopen <= yclose) then begin
|
if (yopen <= yclose) then begin
|
||||||
p := LinePen;
|
p := LinePen;
|
||||||
ADrawer.Brush := FCandleStickUpBrush;
|
ADrawer.Brush := FCandleStickUpBrush;
|
||||||
@ -1418,22 +1476,39 @@ begin
|
|||||||
mCandleStick: DrawCandleStick(x, yopen, yhigh, ylow, yclose, tw);
|
mCandleStick: DrawCandleStick(x, yopen, yhigh, ylow, yclose, tw);
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
GetXYCountNeeded(nx, ny);
|
||||||
|
if Source.YCount > ny then
|
||||||
|
for i := 0 to ny-1 do DrawLabels(ADrawer, i)
|
||||||
|
else
|
||||||
|
DrawLabels(ADrawer);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TOpenHighLowCloseSeries.Extent: TDoubleRect;
|
function TOpenHighLowCloseSeries.Extent: TDoubleRect;
|
||||||
var
|
var
|
||||||
x: Double;
|
x: Double;
|
||||||
tw: Double;
|
tw: Double;
|
||||||
|
j: Integer;
|
||||||
begin
|
begin
|
||||||
if Source.YCount < 4 then exit(EmptyExtent);
|
if Source.YCount < 4 then exit(EmptyExtent);
|
||||||
Result := Source.ExtentList; // axis units
|
Result := Source.ExtentList; // axis units
|
||||||
// Show first and last open/close ticks and candle boxes fully.
|
// Show first and last open/close ticks and candle boxes fully.
|
||||||
x := GetGraphPointX(0); // graph units
|
j := -1;
|
||||||
tw := GetXRange(x, 0) * PERCENT * TickWidth;
|
x := NaN;
|
||||||
|
while IsNaN(x) and (j < Source.Count-1) do begin
|
||||||
|
inc(j);
|
||||||
|
x := GetGraphPointX(j); // graph units
|
||||||
|
end;
|
||||||
|
tw := GetXRange(x, j) * PERCENT * TickWidth;
|
||||||
Result.a.X := Min(Result.a.X, GraphToAxisX(x - tw)); // axis units
|
Result.a.X := Min(Result.a.X, GraphToAxisX(x - tw)); // axis units
|
||||||
// Result.a.X := Min(Result.a.X, x - tw);
|
// Result.a.X := Min(Result.a.X, x - tw);
|
||||||
x := GetGraphPointX(Count - 1);
|
j := Count;
|
||||||
tw := GetXRange(x, Count - 1) * PERCENT * TickWidth;
|
x := NaN;
|
||||||
|
While IsNaN(x) and (j > 0) do begin
|
||||||
|
dec(j);
|
||||||
|
x := GetGraphPointX(j);
|
||||||
|
end;
|
||||||
|
tw := GetXRange(x, j) * PERCENT * TickWidth;
|
||||||
Result.b.X := Max(Result.b.X, AxisToGraphX(x + tw));
|
Result.b.X := Max(Result.b.X, AxisToGraphX(x + tw));
|
||||||
// Result.b.X := Max(Result.b.X, x + tw);
|
// Result.b.X := Max(Result.b.X, x + tw);
|
||||||
end;
|
end;
|
||||||
@ -1604,6 +1679,13 @@ begin
|
|||||||
UpdateParentChart;
|
UpdateParentChart;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
function TOpenHighLowCloseSeries.SkipMissingValues(AIndex: Integer): Boolean;
|
||||||
|
begin
|
||||||
|
Result := IsNaN(Source[AIndex]^.Point);
|
||||||
|
if not Result then
|
||||||
|
Result := HasMissingYValue(AIndex, 4);
|
||||||
|
end;
|
||||||
|
|
||||||
function TOpenHighLowCloseSeries.ToolTargetDistance(
|
function TOpenHighLowCloseSeries.ToolTargetDistance(
|
||||||
const AParams: TNearestPointParams; AGraphPt: TDoublePoint;
|
const AParams: TNearestPointParams; AGraphPt: TDoublePoint;
|
||||||
APointIdx, AXIdx, AYIdx: Integer): Integer;
|
APointIdx, AXIdx, AYIdx: Integer): Integer;
|
||||||
@ -1664,6 +1746,21 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TOpenHighLowCloseSeries.UpdateLabelDirectionReferenceLevel(
|
||||||
|
AIndex, AYIndex: Integer; var ALevel: Double);
|
||||||
|
var
|
||||||
|
item: PChartDataItem;
|
||||||
|
begin
|
||||||
|
if AYIndex = FYIndexLow then
|
||||||
|
ALevel := +Infinity
|
||||||
|
else if AYIndex = FYIndexHigh then
|
||||||
|
ALevel := -Infinity
|
||||||
|
else begin
|
||||||
|
item := Source.Item[AIndex];
|
||||||
|
ALevel := (AxisToGraphY(item^.GetY(FYIndexLow)) + AxisToGraphY(item^.GetY(FYIndexHigh)))*0.5;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
{ TFieldSeries }
|
{ TFieldSeries }
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user