mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-15 18:39:09 +02:00
Merged revision(s) 65456 #b303ecbdd8, 65463 #e03705d02d from trunk:
TAChart/TChartLiveView: Simplify operation of ExtentY modes. ........ TAChart/TChartLiveView: Fix operation when axes have a predefined Range. Modified patch by forum user Muso (https://forum.lazarus.freepascal.org/index.php/topic,55266.msg412075.html#msg412075) ........ git-svn-id: branches/fixes_2_2@65466 -
This commit is contained in:
parent
605bedcdee
commit
d60a55ab43
@ -6,8 +6,8 @@
|
|||||||
</descr>
|
</descr>
|
||||||
</element><element name="TChartLiveView.Active"><short>Allows to turn the scrolling controlled by the component ON and OFF.</short>
|
</element><element name="TChartLiveView.Active"><short>Allows to turn the scrolling controlled by the component ON and OFF.</short>
|
||||||
</element><element name="TChartLiveView.ExtentY"><short>Mode determining how the vertical y axis is displayed in the scrolling viewport</short>
|
</element><element name="TChartLiveView.ExtentY"><short>Mode determining how the vertical y axis is displayed in the scrolling viewport</short>
|
||||||
<descr><p><var>lveAuto</var>: adjusts the height to the visible data range automatically. This works also when the chart contains several y axes.
|
<descr><p><var>lveAuto</var>: adjusts the height to the visible data range automatically. This works also when the chart contains several y axes. Ranges assigned to the axes (by setting <var>axis.Range.Min</var> and <var>axis.Range.UseMin</var>, and/or <var>axis.Range.Max</var> and <var>axis.Range.UseMax</var>) are respected (as long as data points are within this range).
|
||||||
</p><p><var>lveFull</var>: freezes the height to the full extent of the chart.</p><p><var>lveLogical</var>: freezes the height to the logical extent of the chart.</p><p><var>lveMultiAxisRange</var> tries to use a predefined <var>Range</var> of the y axes in case of a multi-axis chart. Use the <var>Range.Min</var>, <var>Range.Max</var>, <var>Range.UseMin</var> and <var>Range.UseMax</var> properties to set up the axis range. Note that the range is extended automatically when a series attached to this axis contains out-of-range y values. When no range is specified (i.e., <var>axis.Range.UseMin=false</var> and <var>axis.Range.UseMax=false</var>) the behaviour is similar to <var>lveAuto</var>.</p>
|
</p><p><var>lveFull</var>: freezes the height to the full extent of the chart.</p><p><var>lveLogical</var>: freezes the height to the logical extent of the chart.</p>
|
||||||
</descr>
|
</descr>
|
||||||
</element><element name="TChartLiveView.Create"><short>Constructor of the <var>TChartLiveView</var>
|
</element><element name="TChartLiveView.Create"><short>Constructor of the <var>TChartLiveView</var>
|
||||||
</short>
|
</short>
|
||||||
|
@ -4,10 +4,10 @@
|
|||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
TChartLiveView is a component optimized for displaying a long array of incoming
|
TChartLiveView is a component optimized for displaying a long array of incoming
|
||||||
data in a viewport with only the most recent data while older data are flowing
|
data in a viewport showing only the most recent data while older data are
|
||||||
to the left out of the viewport.
|
flowing out of the viewport to the left.
|
||||||
|
|
||||||
It was created based on the following forum discussions:
|
It was created on the basis of the following forum discussions:
|
||||||
- https://forum.lazarus.freepascal.org/index.php/topic,15037.html
|
- https://forum.lazarus.freepascal.org/index.php/topic,15037.html
|
||||||
- https://forum.lazarus.freepascal.org/index.php/topic,50759.0.html
|
- https://forum.lazarus.freepascal.org/index.php/topic,50759.0.html
|
||||||
- https://forum.lazarus.freepascal.org/index.php/topic,55266.html
|
- https://forum.lazarus.freepascal.org/index.php/topic,55266.html
|
||||||
@ -29,7 +29,7 @@ uses
|
|||||||
Classes, SysUtils, TAGraph, TAChartUtils;
|
Classes, SysUtils, TAGraph, TAChartUtils;
|
||||||
|
|
||||||
type
|
type
|
||||||
TChartLiveViewExtentY = (lveAuto, lveFull, lveLogical, lveMultiAxisRange);
|
TChartLiveViewExtentY = (lveAuto, lveFull, lveLogical);
|
||||||
|
|
||||||
{ TChartLiveView }
|
{ TChartLiveView }
|
||||||
|
|
||||||
@ -52,13 +52,13 @@ type
|
|||||||
procedure SetChart(const AValue: TChart);
|
procedure SetChart(const AValue: TChart);
|
||||||
procedure SetExtentY(const AValue: TChartLiveViewExtentY);
|
procedure SetExtentY(const AValue: TChartLiveViewExtentY);
|
||||||
procedure SetViewportSize(const AValue: Double);
|
procedure SetViewportSize(const AValue: Double);
|
||||||
procedure UpdateViewport;
|
|
||||||
protected
|
protected
|
||||||
procedure RestoreAxisRanges;
|
procedure UpdateViewport; virtual;
|
||||||
procedure StoreAxisRanges;
|
|
||||||
public
|
public
|
||||||
constructor Create(AOwner: TComponent); override;
|
constructor Create(AOwner: TComponent); override;
|
||||||
destructor Destroy; override;
|
destructor Destroy; override;
|
||||||
|
procedure RestoreAxisRanges;
|
||||||
|
procedure StoreAxisRanges;
|
||||||
published
|
published
|
||||||
property Active: Boolean read FActive write SetActive default false;
|
property Active: Boolean read FActive write SetActive default false;
|
||||||
property Chart: TChart read FChart write SetChart default nil;
|
property Chart: TChart read FChart write SetChart default nil;
|
||||||
@ -82,6 +82,7 @@ end;
|
|||||||
|
|
||||||
destructor TChartLiveView.Destroy;
|
destructor TChartLiveView.Destroy;
|
||||||
begin
|
begin
|
||||||
|
RestoreAxisRanges;
|
||||||
FreeAndNil(FListener);
|
FreeAndNil(FListener);
|
||||||
inherited;
|
inherited;
|
||||||
end;
|
end;
|
||||||
@ -95,6 +96,11 @@ begin
|
|||||||
UpdateViewport;
|
UpdateViewport;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ The ChartLiveView may change the Range properties of an axis. The original
|
||||||
|
values, before applying the live view, are restored here from internal
|
||||||
|
variables.
|
||||||
|
Be careful when calling this procedure in user code, it may disrupt the
|
||||||
|
operation of the live view. }
|
||||||
procedure TChartLiveView.RestoreAxisRanges;
|
procedure TChartLiveView.RestoreAxisRanges;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
@ -151,8 +157,9 @@ end;
|
|||||||
procedure TChartLiveview.SetExtentY(const AValue: TChartLiveViewExtentY);
|
procedure TChartLiveview.SetExtentY(const AValue: TChartLiveViewExtentY);
|
||||||
begin
|
begin
|
||||||
if FExtentY = AValue then exit;
|
if FExtentY = AValue then exit;
|
||||||
|
if FExtentY = lveAuto then
|
||||||
|
RestoreAxisRanges;
|
||||||
FExtentY := AValue;
|
FExtentY := AValue;
|
||||||
RestoreAxisRanges;
|
|
||||||
FullExtentChanged(nil);
|
FullExtentChanged(nil);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -163,6 +170,10 @@ begin
|
|||||||
FullExtentChanged(nil);
|
FullExtentChanged(nil);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ The ChartLiveView may change the Range properties of an axis. The original
|
||||||
|
values, before applying the live view, are stored here in internal variables.
|
||||||
|
Be careful when calling this procedure in user code, it may disrupt the
|
||||||
|
operation of the live view. }
|
||||||
procedure TChartLiveView.StoreAxisRanges;
|
procedure TChartLiveView.StoreAxisRanges;
|
||||||
var
|
var
|
||||||
i: Integer;
|
i: Integer;
|
||||||
@ -187,11 +198,14 @@ var
|
|||||||
fext, lext: TDoubleRect; // "full extent", "logical extent" variables
|
fext, lext: TDoubleRect; // "full extent", "logical extent" variables
|
||||||
w: double;
|
w: double;
|
||||||
i, j: Integer;
|
i, j: Integer;
|
||||||
ymin, ymax: Double;
|
yminAx, ymaxAx, yminSer, ymaxSer: Double;
|
||||||
ygmin, ygmax: Double;
|
dy: Double;
|
||||||
ser: TChartSeries;
|
ser: TChartSeries;
|
||||||
ax: TChartAxis;
|
axis: TChartAxis;
|
||||||
begin
|
begin
|
||||||
|
if csDesigning in ComponentState then
|
||||||
|
exit;
|
||||||
|
|
||||||
if not FChart.ScalingValid then
|
if not FChart.ScalingValid then
|
||||||
exit;
|
exit;
|
||||||
|
|
||||||
@ -204,72 +218,94 @@ begin
|
|||||||
w := lext.b.x - lext.a.x
|
w := lext.b.x - lext.a.x
|
||||||
else
|
else
|
||||||
w := FViewportSize;
|
w := FViewportSize;
|
||||||
|
// Move the extent to the right
|
||||||
lext.b.x := fext.b.x;
|
lext.b.x := fext.b.x;
|
||||||
lext.a.x := lext.b.X - w;
|
lext.a.x := lext.b.x - w;
|
||||||
if lext.a.x < fext.a.x then begin
|
if lext.a.x < fext.a.x then begin
|
||||||
lext.a.x := fext.a.x;
|
lext.a.x := fext.a.x;
|
||||||
lext.b.x := lext.a.x + w;
|
lext.b.x := lext.a.x + w;
|
||||||
end;
|
end;
|
||||||
case FExtentY of
|
case FExtentY of
|
||||||
|
lveAuto:
|
||||||
|
// The aim of lveAuto is to determine the y-axis range according to
|
||||||
|
// the ymin/ymax of the series connected to the axis
|
||||||
|
begin
|
||||||
|
lext.a.y := fext.a.y;
|
||||||
|
lext.b.y := fext.b.y;
|
||||||
|
for i := 0 to FChart.AxisList.Count-1 do
|
||||||
|
begin
|
||||||
|
axis := FChart.AxisList[i];
|
||||||
|
// Ignore x-axes
|
||||||
|
if (axis.Alignment in [calTop, calBottom]) then
|
||||||
|
Continue;
|
||||||
|
ymaxAx := -Infinity;
|
||||||
|
yminAx := Infinity;
|
||||||
|
// Step through all active non-rotated series attached to this axis
|
||||||
|
for j := 0 to FChart.SeriesCount-1 do
|
||||||
|
begin
|
||||||
|
if FChart.Series[j] is TChartSeries then
|
||||||
|
begin
|
||||||
|
ser := TChartSeries(FChart.Series[j]);
|
||||||
|
if (not ser.Active) or (ser.GetAxisY <> axis) or ser.IsRotated then
|
||||||
|
continue;
|
||||||
|
yminSer := Infinity;
|
||||||
|
ymaxSer := -Infinity;
|
||||||
|
ser.FindYRange(lext.a.x, lext.b.x, yminSer, ymaxSer);
|
||||||
|
yminAx := Min(yminAx, yminSer);
|
||||||
|
ymaxAx := Max(ymaxAx, ymaxSer);
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
// Only if an axis has no active non-rotated series, we have -infinity
|
||||||
|
if ymaxAx > -Infinity then
|
||||||
|
begin
|
||||||
|
if ymaxAx = yminAx then
|
||||||
|
begin
|
||||||
|
if yminAx = 0 then
|
||||||
|
begin
|
||||||
|
yminAx := -1;
|
||||||
|
ymaxAx := +1;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
// Set the range to 10% around the value, take care of the sign!
|
||||||
|
begin
|
||||||
|
dy := abs(yminAx) * 0.1;
|
||||||
|
yminAx := yminAx - dy;
|
||||||
|
ymaxAx := ymaxAx + dy;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
begin
|
||||||
|
yminAx := -1;
|
||||||
|
ymaxAx := +1;
|
||||||
|
end;
|
||||||
|
// Only if the user did not set its own range we set the axis range
|
||||||
|
// determined above.
|
||||||
|
if (not FAxisRanges[i].UseMin) then
|
||||||
|
begin
|
||||||
|
axis.Range.Min := yminAx;
|
||||||
|
axis.Range.UseMin := true; // we had stored the original UseMin in FAxisRanges
|
||||||
|
lext.a.y := axis.GetTransform.AxisToGraph(yminAx);
|
||||||
|
end;
|
||||||
|
if (not FAxisRanges[i].UseMax) then
|
||||||
|
begin
|
||||||
|
axis.Range.Max := ymaxAx;
|
||||||
|
axis.Range.UseMax := true; // we had stored the original UseMax in FAxisRanges
|
||||||
|
lext.b.y := axis.GetTransform.AxisToGraph(ymaxAx);
|
||||||
|
end;
|
||||||
|
end; // series loop
|
||||||
|
end; // axes loop
|
||||||
|
|
||||||
lveFull:
|
lveFull:
|
||||||
begin
|
begin
|
||||||
lext.a.y := fext.a.y;
|
lext.a.y := fext.a.y;
|
||||||
lext.b.y := fext.b.y;
|
lext.b.y := fext.b.y;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
lveLogical:
|
lveLogical:
|
||||||
;
|
;
|
||||||
lveAuto,
|
|
||||||
lveMultiAxisRange:
|
|
||||||
begin
|
|
||||||
lext.a.y := Infinity;
|
|
||||||
lext.b.y := -Infinity;
|
|
||||||
for i := 0 to FChart.AxisList.Count-1 do
|
|
||||||
begin
|
|
||||||
ax := FChart.AxisList[i];
|
|
||||||
// we only support scrolling along x, i.e. ax must be a y axis.
|
|
||||||
if not (ax.Alignment in [calLeft, calRight]) then
|
|
||||||
Continue;
|
|
||||||
ymin := Infinity;
|
|
||||||
ymax := -Infinity;
|
|
||||||
for j := 0 to FChart.SeriesCount-1 do
|
|
||||||
if FChart.Series[j] is TChartSeries then
|
|
||||||
begin
|
|
||||||
ser := TChartSeries(FChart.Series[j]);
|
|
||||||
if (not ser.Active) or (ser.GetAxisY <> ax) or ser.IsRotated then
|
|
||||||
continue;
|
|
||||||
ser.FindYRange(lext.a.x, lext.b.x, ymin, ymax);
|
|
||||||
end;
|
|
||||||
if (ymin = Infinity) then
|
|
||||||
begin
|
|
||||||
ymin := -1;
|
|
||||||
ymax := +1;
|
|
||||||
end else
|
|
||||||
if (ymin = ymax) then
|
|
||||||
begin
|
|
||||||
if ymin = 0 then
|
|
||||||
begin
|
|
||||||
ymin := -1;
|
|
||||||
ymax := +1;
|
|
||||||
end else
|
|
||||||
begin
|
|
||||||
ymin := abs(ymin) * 0.9;
|
|
||||||
ymax := abs(ymax) * 1.1;
|
|
||||||
end;
|
|
||||||
end;
|
|
||||||
ax.Range.Min := ymin;
|
|
||||||
ax.Range.Max := ymax;
|
|
||||||
if (FExtentY = lveMultiAxisRange) then
|
|
||||||
begin
|
|
||||||
if FAxisRanges[i].UseMin then ax.Range.Min := FAxisRanges[i].Min;
|
|
||||||
if FAxisRanges[i].UseMax then ax.Range.Max := FAxisRanges[i].Max;
|
|
||||||
end;
|
|
||||||
ax.Range.UseMin := true;
|
|
||||||
ax.Range.UseMax := true;
|
|
||||||
lext.a.y := Min(lext.a.y, ax.GetTransform.AxisToGraph(ymin));
|
|
||||||
lext.b.y := Max(lext.b.y, ax.GetTransform.AxisToGraph(ymax));
|
|
||||||
end; // for i
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
FChart.LogicalExtent := lext;
|
FChart.LogicalExtent := lext;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user