mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-12 01:16:01 +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>
|
||||
</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>
|
||||
<descr><p><var>lveAuto</var>: adjusts the height to the visible data range automatically. This works also when the chart contains several y axes.
|
||||
</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>
|
||||
<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>
|
||||
</descr>
|
||||
</element><element name="TChartLiveView.Create"><short>Constructor of the <var>TChartLiveView</var>
|
||||
</short>
|
||||
|
@ -4,10 +4,10 @@
|
||||
-------------------
|
||||
|
||||
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
|
||||
to the left out of the viewport.
|
||||
data in a viewport showing only the most recent data while older data are
|
||||
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,50759.0.html
|
||||
- https://forum.lazarus.freepascal.org/index.php/topic,55266.html
|
||||
@ -29,7 +29,7 @@ uses
|
||||
Classes, SysUtils, TAGraph, TAChartUtils;
|
||||
|
||||
type
|
||||
TChartLiveViewExtentY = (lveAuto, lveFull, lveLogical, lveMultiAxisRange);
|
||||
TChartLiveViewExtentY = (lveAuto, lveFull, lveLogical);
|
||||
|
||||
{ TChartLiveView }
|
||||
|
||||
@ -52,13 +52,13 @@ type
|
||||
procedure SetChart(const AValue: TChart);
|
||||
procedure SetExtentY(const AValue: TChartLiveViewExtentY);
|
||||
procedure SetViewportSize(const AValue: Double);
|
||||
procedure UpdateViewport;
|
||||
protected
|
||||
procedure RestoreAxisRanges;
|
||||
procedure StoreAxisRanges;
|
||||
procedure UpdateViewport; virtual;
|
||||
public
|
||||
constructor Create(AOwner: TComponent); override;
|
||||
destructor Destroy; override;
|
||||
procedure RestoreAxisRanges;
|
||||
procedure StoreAxisRanges;
|
||||
published
|
||||
property Active: Boolean read FActive write SetActive default false;
|
||||
property Chart: TChart read FChart write SetChart default nil;
|
||||
@ -82,6 +82,7 @@ end;
|
||||
|
||||
destructor TChartLiveView.Destroy;
|
||||
begin
|
||||
RestoreAxisRanges;
|
||||
FreeAndNil(FListener);
|
||||
inherited;
|
||||
end;
|
||||
@ -95,6 +96,11 @@ begin
|
||||
UpdateViewport;
|
||||
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;
|
||||
var
|
||||
i: Integer;
|
||||
@ -151,8 +157,9 @@ end;
|
||||
procedure TChartLiveview.SetExtentY(const AValue: TChartLiveViewExtentY);
|
||||
begin
|
||||
if FExtentY = AValue then exit;
|
||||
if FExtentY = lveAuto then
|
||||
RestoreAxisRanges;
|
||||
FExtentY := AValue;
|
||||
RestoreAxisRanges;
|
||||
FullExtentChanged(nil);
|
||||
end;
|
||||
|
||||
@ -163,6 +170,10 @@ begin
|
||||
FullExtentChanged(nil);
|
||||
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;
|
||||
var
|
||||
i: Integer;
|
||||
@ -187,11 +198,14 @@ var
|
||||
fext, lext: TDoubleRect; // "full extent", "logical extent" variables
|
||||
w: double;
|
||||
i, j: Integer;
|
||||
ymin, ymax: Double;
|
||||
ygmin, ygmax: Double;
|
||||
yminAx, ymaxAx, yminSer, ymaxSer: Double;
|
||||
dy: Double;
|
||||
ser: TChartSeries;
|
||||
ax: TChartAxis;
|
||||
axis: TChartAxis;
|
||||
begin
|
||||
if csDesigning in ComponentState then
|
||||
exit;
|
||||
|
||||
if not FChart.ScalingValid then
|
||||
exit;
|
||||
|
||||
@ -204,72 +218,94 @@ begin
|
||||
w := lext.b.x - lext.a.x
|
||||
else
|
||||
w := FViewportSize;
|
||||
// Move the extent to the right
|
||||
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
|
||||
lext.a.x := fext.a.x;
|
||||
lext.b.x := lext.a.x + w;
|
||||
end;
|
||||
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:
|
||||
begin
|
||||
lext.a.y := fext.a.y;
|
||||
lext.b.y := fext.b.y;
|
||||
end;
|
||||
|
||||
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;
|
||||
|
||||
FChart.LogicalExtent := lext;
|
||||
end;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user