TLazy + Data-Endpoints refactoring

This commit is contained in:
Michael Schimmel
2025-07-17 00:48:46 +02:00
parent abad66ae52
commit b3359a4d73
8 changed files with 271 additions and 250 deletions
+59 -70
View File
@@ -26,26 +26,18 @@ type
type
TPanel = class;
// Abstract base for a data series within the chart.
TSeries = class abstract(TObject)
protected
function GetCount: Int64; virtual; abstract;
function GetTotalCount: Int64; virtual; abstract;
public
property Count: Int64 read GetCount;
property TotalCount: Int64 read GetTotalCount;
end;
// Abstract base for a drawable layer in a panel.
TLayer = class abstract(TObject)
protected
function GetCount: Int64; virtual; abstract;
function GetTotalCount: Int64; virtual; abstract;
function GetOwner: TMycChart; virtual; abstract;
function GetSeries: TSeries; virtual; abstract;
procedure Update; virtual; abstract;
function Update: Boolean; virtual; abstract;
public
procedure Repaint;
property Count: Int64 read GetCount;
property TotalCount: Int64 read GetTotalCount;
property Owner: TMycChart read GetOwner;
property Series: TSeries read GetSeries;
end;
TDataLayer = class abstract(TLayer)
@@ -93,12 +85,12 @@ type
TPanel = class(TObject)
private
FOwner: TMycChart;
FSeriesList: TObjectList<TMycChart.TDataLayer>;
FLayers: TObjectList<TMycChart.TDataLayer>;
FWeight: Single;
procedure SetWeight(const Value: Single);
protected
procedure Paint(const Canvas: TCanvas; const Viewport: TRectF; MasterTotalCount: Int64; ViewStartIndex, ViewCount: Int64);
procedure Update(MasterTotalCount: Int64; ViewStartIndex: Int64; out SeriesChanged, SeriesRepaint: Boolean);
function Update: Boolean;
public
constructor Create(AOwner: TMycChart; AWeight: Single);
@@ -144,6 +136,7 @@ type
protected
procedure Paint; override;
procedure DoIdle;
function Update: Boolean;
procedure DoMouseLeave; override;
procedure MouseWheel(Shift: TShiftState; WheelDelta: Integer; var Handled: Boolean); override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Single); override;
@@ -168,6 +161,7 @@ type
property JumpButtonRect: TRectF read FJumpButtonRect;
property ViewCount: Int64 read FViewCount;
property ViewStartIndex: Int64 read FViewStartIndex;
property XAxisSeries: TMycChart.TXAxisLayer read FXAxisSeries;
end;
implementation
@@ -230,7 +224,7 @@ begin
Result.Point.X := X;
Result.Point.Y := Y;
Result.Idx := Round((rect.Right - X) * (FViewCount - 1) / rect.Width);
if (Result.Idx < 0) or (Result.Idx >= FXAxisSeries.Series.Count) then
if (Result.Idx < 0) or (Result.Idx >= FXAxisSeries.Count) then
Result.Idx := -1;
Result.BarX := NaN;
if Result.Idx >= 0 then
@@ -248,34 +242,34 @@ begin
end;
procedure TMycChart.DoIdle;
var
prevTotalCount, newTotalCount: Int64;
seriesChanged, seriesRepaint, repaintSignal: Boolean;
countDelta: Int64;
begin
if Update then
Repaint;
end;
function TMycChart.Update: Boolean;
begin
if not Assigned(FXAxisSeries) then
exit;
exit(false);
prevTotalCount := FXAxisSeries.Series.TotalCount;
FXAxisSeries.Update;
newTotalCount := FXAxisSeries.Series.TotalCount;
var prevTotalCount := FXAxisSeries.TotalCount;
var seriesChanged := false;
seriesChanged := prevTotalCount <> newTotalCount;
seriesRepaint := (FViewStartIndex = 0);
if FXAxisSeries.Update then
seriesChanged := (FViewStartIndex = 0);
for var panel in FPanelList do
begin
var panelChanged, panelRepaint: Boolean;
panel.Update(prevTotalCount, FViewStartIndex, panelChanged, panelRepaint);
seriesChanged := seriesChanged or panelChanged;
seriesRepaint := seriesRepaint or panelRepaint;
if panel.Update then
seriesChanged := true;
end;
// When we are not displaying live data, adjust start index accordingly
if (FViewStartIndex > 0) then
if FViewStartIndex > 0 then
begin
countDelta := newTotalCount - prevTotalCount;
if (countDelta > 0) then
var newTotalCount := FXAxisSeries.TotalCount;
var countDelta := newTotalCount - prevTotalCount;
if countDelta > 0 then
begin
FViewStartIndex := FViewStartIndex + countDelta;
if (FViewStartIndex + FViewCount > newTotalCount) then
@@ -283,10 +277,9 @@ begin
end;
end;
repaintSignal := FNeedRepaint.Reset;
var repaintSignal := FNeedRepaint.Reset;
if repaintSignal or (seriesRepaint and seriesChanged) then
Repaint;
Result := repaintSignal or seriesChanged;
end;
procedure TMycChart.DoMouseLeave;
@@ -312,14 +305,14 @@ begin
inherited;
rect := Self.LocalRect;
if (not Assigned(FXAxisSeries)) or (FXAxisSeries.Series.Count <= 1) or (FViewCount <= 1) or (PanelCount = 0) then
if (not Assigned(FXAxisSeries)) or (FXAxisSeries.Count <= 1) or (FViewCount <= 1) or (PanelCount = 0) then
begin
Canvas.Fill.Color := TAlphaColors.Gray;
Canvas.FillText(rect, 'No Data', false, 1, [], TTextAlign.Center, TTextAlign.Center);
Exit;
end;
masterTotalCount := FXAxisSeries.Series.TotalCount;
masterTotalCount := FXAxisSeries.TotalCount;
// Calculate total weight for proportional panel height
totalWeight := 0;
@@ -332,6 +325,7 @@ begin
for var i := 0 to PanelCount - 1 do
begin
var panel := Panels[i];
panelHeight := panel.Weight * rect.Height / totalWeight;
panelRect := TRectF.Create(rect.Left, currentY, rect.Right, currentY + panelHeight);
@@ -542,7 +536,7 @@ begin
FViewStartIndex := FViewStartIndex - indexDelta;
// Clamp values
maxIndex := FXAxisSeries.Series.Count - FViewCount;
maxIndex := FXAxisSeries.Count - FViewCount;
if (maxIndex < 0) then
maxIndex := 0;
if (FViewStartIndex < 0) then
@@ -602,7 +596,7 @@ begin
if not Assigned(FXAxisSeries) then
exit;
var xAxisCount := FXAxisSeries.Series.Count;
var xAxisCount := FXAxisSeries.Count;
if (xAxisCount = 0) or (Width <= 0) then
exit;
@@ -667,13 +661,13 @@ constructor TMycChart.TPanel.Create(AOwner: TMycChart; AWeight: Single);
begin
inherited Create;
FOwner := AOwner;
FSeriesList := TObjectList<TMycChart.TDataLayer>.Create(true);
FLayers := TObjectList<TMycChart.TDataLayer>.Create(true);
FWeight := AWeight;
end;
destructor TMycChart.TPanel.Destroy;
begin
FSeriesList.Free;
FLayers.Free;
inherited Destroy;
end;
@@ -684,7 +678,7 @@ function TMycChart.TPanel.AddDoubleSeries(
): TMycChart.TDataLayer;
begin
Result := TChartLineLayer.Create(Self, DataProvider, ALineColor, ALineWidth);
FSeriesList.Add(Result);
FLayers.Add(Result);
end;
function TMycChart.TPanel.AddOhlcSeries(
@@ -695,7 +689,7 @@ function TMycChart.TPanel.AddOhlcSeries(
begin
Result :=
TChartOhlcLayer.Create(Self, DataProvider, TMutable<TAlphaColor>.Constant(AUpColor), TMutable<TAlphaColor>.Constant(ADownColor));
FSeriesList.Add(Result);
FLayers.Add(Result);
end;
procedure TMycChart.TPanel.Paint(const Canvas: TCanvas; const Viewport: TRectF; MasterTotalCount, ViewStartIndex, ViewCount: Int64);
@@ -706,16 +700,16 @@ var
xTransform: TFunc<Int64, Single>;
yTransform: TFunc<Double, Single>;
function CalcView(Series: TMycChart.TSeries; out First, Last: Int64): Boolean;
function CalcView(Layer: TMycChart.TDataLayer; out First, Last: Int64): Boolean;
var
seriesTotalCount, offset, startIdx: Int64;
begin
seriesTotalCount := series.TotalCount;
seriesTotalCount := Layer.TotalCount;
offset := MasterTotalCount - seriesTotalCount;
startIdx := ViewStartIndex - offset;
First := Max(0, startIdx);
Last := Min(series.Count, startIdx + ViewCount) - 1;
Last := Min(Layer.Count, startIdx + ViewCount) - 1;
Result := First <= Last;
end;
@@ -724,12 +718,16 @@ begin
localMin := 0;
localMax := 0;
// Update data state
for var layer in FLayers do
layer.Update;
// 1. Calculate value range (Y-Axis) for this panel only
for var series in FSeriesList do
for var layer in FLayers do
begin
if CalcView(series.Series, seriesFirst, seriesLast) then
if CalcView(layer, seriesFirst, seriesLast) then
begin
if series.GetValueRange(seriesFirst, seriesLast, seriesMin, seriesMax) then
if layer.GetValueRange(seriesFirst, seriesLast, seriesMin, seriesMax) then
begin
if not rangeInitialized then
begin
@@ -760,18 +758,18 @@ begin
yTransform := function(value: Double): Single begin Result := top + (1 - (value - localMin) / range) * height; end;
// 3. Paint all layers in this panel
for var series in FSeriesList do
for var layer in FLayers do
begin
if CalcView(series.Series, seriesFirst, seriesLast) then
if CalcView(layer, seriesFirst, seriesLast) then
begin
var offset := MasterTotalCount - series.Series.TotalCount;
var offset := MasterTotalCount - layer.TotalCount;
xTransform :=
function(index: Int64): Single
begin
Result := Viewport.Right - (((index + offset - ViewStartIndex) / (ViewCount - 1)) * Viewport.Width);
end;
series.Paint(Canvas, seriesFirst, seriesLast, xTransform, yTransform);
layer.Paint(Canvas, seriesFirst, seriesLast, xTransform, yTransform);
end;
end;
end;
@@ -789,23 +787,14 @@ begin
end;
end;
procedure TMycChart.TPanel.Update(MasterTotalCount: Int64; ViewStartIndex: Int64; out SeriesChanged, SeriesRepaint: Boolean);
var
seriesCount: Int64;
function TMycChart.TPanel.Update: Boolean;
begin
SeriesChanged := false;
SeriesRepaint := false;
for var series in FSeriesList do
Result := false;
for var layer in FLayers do
begin
seriesCount := series.Series.TotalCount;
if (ViewStartIndex <= MasterTotalCount - seriesCount) then
SeriesRepaint := true;
series.Update;
if (seriesCount <> series.Series.TotalCount) then
SeriesChanged := true;
if layer.Update then
if (Owner.ViewStartIndex <= Owner.FXAxisSeries.TotalCount - layer.TotalCount) then
Result := true;
end;
end;
@@ -846,7 +835,7 @@ var
textSize: TRectF;
begin
// Do not draw if no data or view is invalid
if (not Assigned(Series)) or (Series.Count = 0) then
if GetCount = 0 then
exit;
// 1. Snap X to the candle's center