mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-11-03 12:59:40 +01:00
TAChart: Add TCalculatedChartSource.AccumulationDirection property. Add tests.
git-svn-id: trunk@32396 -
This commit is contained in:
parent
60590c7530
commit
27ff423301
@ -191,10 +191,10 @@ type
|
||||
procedure AddFirst(const AItem: TChartDataItem);
|
||||
procedure AddLast(const AItem: TChartDataItem);
|
||||
procedure Clear; inline;
|
||||
function GetPLast: PChartDataItem; overload;
|
||||
function GetPLast(AOffset: Cardinal): PChartDataItem; overload;
|
||||
function GetPtr(AOffset: Cardinal): PChartDataItem; overload;
|
||||
procedure GetSum(var AItem: TChartDataItem);
|
||||
procedure RemoveLast; overload;
|
||||
procedure RemoveFirst;
|
||||
procedure RemoveLast;
|
||||
procedure RemoveValue(const AItem: TChartDataItem);
|
||||
property Capacity: Cardinal read GetCapacity write SetCapacity;
|
||||
end;
|
||||
@ -415,6 +415,7 @@ end;
|
||||
procedure TChartSourceBuffer.Clear;
|
||||
begin
|
||||
FCount := 0;
|
||||
FStart := 0;
|
||||
FSum.Y := 0;
|
||||
FSum.YList := nil;
|
||||
end;
|
||||
@ -429,25 +430,28 @@ begin
|
||||
Result := Length(FBuf);
|
||||
end;
|
||||
|
||||
function TChartSourceBuffer.GetPLast(AOffset: Cardinal): PChartDataItem;
|
||||
function TChartSourceBuffer.GetPtr(AOffset: Cardinal): PChartDataItem;
|
||||
begin
|
||||
if AOffset >= FCount then
|
||||
raise EBufferError.Create('AOffset');
|
||||
AOffset := FCount - 1 - AOffset;
|
||||
Result := @FBuf[(FStart + AOffset + Capacity) mod Capacity];
|
||||
end;
|
||||
|
||||
function TChartSourceBuffer.GetPLast: PChartDataItem;
|
||||
begin
|
||||
Result := @FBuf[EndIndex];
|
||||
end;
|
||||
|
||||
procedure TChartSourceBuffer.GetSum(var AItem: TChartDataItem);
|
||||
begin
|
||||
AItem.Y := FSum.Y;
|
||||
AItem.YList := Copy(FSum.YList);
|
||||
end;
|
||||
|
||||
procedure TChartSourceBuffer.RemoveFirst;
|
||||
begin
|
||||
if FCount = 0 then
|
||||
raise EBufferError.Create('Empty');
|
||||
RemoveValue(FBuf[FStart]);
|
||||
FCount -= 1;
|
||||
FStart := (FStart + 1) mod Capacity;
|
||||
end;
|
||||
|
||||
procedure TChartSourceBuffer.RemoveLast;
|
||||
begin
|
||||
if FCount = 0 then
|
||||
|
||||
@ -158,11 +158,13 @@ type
|
||||
end;
|
||||
|
||||
TChartAccumulationMethod = (camNone, camSum, camAverage, camDerivative);
|
||||
TChartAccumulationDirection = (cadBackward, cadForward, cadCenter);
|
||||
|
||||
{ TCalculatedChartSource }
|
||||
|
||||
TCalculatedChartSource = class(TCustomChartSource)
|
||||
strict private
|
||||
FAccumulationDirection: TChartAccumulationDirection;
|
||||
FAccumulationMethod: TChartAccumulationMethod;
|
||||
FAccumulationRange: Cardinal;
|
||||
FHistory: TChartSourceBuffer;
|
||||
@ -179,7 +181,10 @@ type
|
||||
procedure CalcDerivative(var AIndex: Integer);
|
||||
procedure CalcPercentage;
|
||||
procedure Changed(ASender: TObject);
|
||||
function EffectiveAccumulationRange: Cardinal;
|
||||
procedure ExtractItem(out AItem: TChartDataItem; AIndex: Integer);
|
||||
procedure RangeAround(AIndex: Integer; out ALeft, ARight: Integer);
|
||||
procedure SetAccumulationDirection(AValue: TChartAccumulationDirection);
|
||||
procedure SetAccumulationMethod(AValue: TChartAccumulationMethod);
|
||||
procedure SetAccumulationRange(AValue: Cardinal);
|
||||
procedure SetOrigin(AValue: TCustomChartSource);
|
||||
@ -196,6 +201,9 @@ type
|
||||
|
||||
function IsSorted: Boolean; override;
|
||||
published
|
||||
property AccumulationDirection: TChartAccumulationDirection
|
||||
read FAccumulationDirection write SetAccumulationDirection
|
||||
default cadBackward;
|
||||
property AccumulationMethod: TChartAccumulationMethod
|
||||
read FAccumulationMethod write SetAccumulationMethod default camNone;
|
||||
property AccumulationRange: Cardinal
|
||||
@ -214,6 +222,9 @@ implementation
|
||||
uses
|
||||
Math, StrUtils, SysUtils;
|
||||
|
||||
const
|
||||
MAX_DERIVATIVE_RANGE = 10;
|
||||
|
||||
type
|
||||
|
||||
{ TListChartSourceStrings }
|
||||
@ -854,47 +865,58 @@ end;
|
||||
|
||||
procedure TCalculatedChartSource.CalcAccumulation(AIndex: Integer);
|
||||
var
|
||||
i, ar: Integer;
|
||||
i, oldLeft, oldRight, newLeft, newRight: Integer;
|
||||
begin
|
||||
if (AccumulationMethod = camDerivative) and (AccumulationRange = 0) then
|
||||
FHistory.Capacity := 10
|
||||
if AccumulationDirection = cadCenter then
|
||||
FHistory.Capacity := EffectiveAccumulationRange * 2
|
||||
else
|
||||
FHistory.Capacity := AccumulationRange;
|
||||
ar := IfThen(AccumulationRange = 0, MaxInt, AccumulationRange);
|
||||
if FIndex = AIndex - 1 then begin
|
||||
ExtractItem(FItem, AIndex);
|
||||
FHistory.AddLast(FItem);
|
||||
end
|
||||
else if FIndex = AIndex + 1 then begin
|
||||
if AccumulationRange = 0 then begin
|
||||
ExtractItem(FItem, FIndex);
|
||||
FHistory.RemoveValue(FItem);
|
||||
ExtractItem(FItem, AIndex);
|
||||
end
|
||||
else begin
|
||||
i := AIndex - AccumulationRange + 1;
|
||||
if i < 0 then
|
||||
FHistory.RemoveLast
|
||||
else begin
|
||||
ExtractItem(FItem, i);
|
||||
FHistory.AddFirst(FItem);
|
||||
end;
|
||||
FItem := FHistory.GetPLast^;
|
||||
FHistory.Capacity := EffectiveAccumulationRange;
|
||||
RangeAround(FIndex, oldLeft, oldRight);
|
||||
RangeAround(AIndex, newLeft, newRight);
|
||||
if
|
||||
(FIndex < 0) or (Abs(oldLeft - newLeft) > 1) or
|
||||
(Abs(oldRight - newRight) > 1)
|
||||
then begin
|
||||
FHistory.Clear;
|
||||
for i := newLeft to newRight do begin
|
||||
ExtractItem(FItem, i);
|
||||
FHistory.AddLast(FItem);
|
||||
end;
|
||||
end
|
||||
else begin
|
||||
FHistory.Clear;
|
||||
for i := Max(AIndex - ar + 1, 0) to AIndex do begin
|
||||
if FHistory.Capacity = 0 then
|
||||
for i := oldLeft to newLeft - 1 do begin
|
||||
ExtractItem(FItem, i);
|
||||
FHistory.RemoveValue(FItem);
|
||||
end
|
||||
else
|
||||
for i := oldLeft to newLeft - 1 do
|
||||
FHistory.RemoveFirst;
|
||||
if FHistory.Capacity = 0 then
|
||||
for i := oldRight downto newRight + 1 do begin
|
||||
ExtractItem(FItem, i);
|
||||
FHistory.RemoveValue(FItem);
|
||||
end
|
||||
else
|
||||
for i := oldRight downto newRight + 1 do
|
||||
FHistory.RemoveLast;
|
||||
for i := oldLeft - 1 downto newLeft do begin
|
||||
ExtractItem(FItem, i);
|
||||
FHistory.AddFirst(FItem);
|
||||
end;
|
||||
for i := oldRight + 1 to newRight do begin
|
||||
ExtractItem(FItem, i);
|
||||
FHistory.AddLast(FItem);
|
||||
end;
|
||||
end;
|
||||
if (AIndex <> newLeft) and (AIndex <> newRight) then
|
||||
ExtractItem(FItem, AIndex);
|
||||
case AccumulationMethod of
|
||||
camSum:
|
||||
FHistory.GetSum(FItem);
|
||||
camAverage: begin
|
||||
FHistory.GetSum(FItem);
|
||||
FItem.MultiplyY(1 / Min(ar, AIndex + 1));
|
||||
FItem.MultiplyY(1 / (newRight - newLeft + 1));
|
||||
end;
|
||||
camDerivative:
|
||||
CalcDerivative(AIndex);
|
||||
@ -915,14 +937,15 @@ const
|
||||
( 49/20, -6, 15/2, -20/3, 15/4, -6/5, 1/6));
|
||||
var
|
||||
prevItem: PChartDataItem;
|
||||
i, j, ar: Integer;
|
||||
i, j, ar, iLeft, iRight: Integer;
|
||||
dx: Double;
|
||||
begin
|
||||
if AIndex = 0 then begin
|
||||
RangeAround(AIndex, iLeft, iRight);
|
||||
if (AccumulationDirection <> cadBackward) or (AIndex = 0) then begin
|
||||
FItem.SetY(SafeNan);
|
||||
exit;
|
||||
end;
|
||||
dx := FItem.X - FHistory.GetPLast(1)^.X;
|
||||
dx := FItem.X - FHistory.GetPtr(AIndex - iLeft - 1)^.X;
|
||||
if dx = 0 then begin
|
||||
FItem.SetY(SafeNan);
|
||||
exit;
|
||||
@ -931,7 +954,7 @@ begin
|
||||
ar := IfThen(AccumulationRange = 0, MaxInt, AccumulationRange);
|
||||
ar := MinValue([ar, Integer(AIndex + 1), High(COEFFS)]);
|
||||
for j := 0 to ar - 1 do begin
|
||||
prevItem := FHistory.GetPLast(j);
|
||||
prevItem := FHistory.GetPtr(AIndex - iLeft - j);
|
||||
FItem.Y += prevItem^.Y * COEFFS[ar, j];
|
||||
for i := 0 to High(FItem.YList) do
|
||||
FItem.YList[i] += prevItem^.YList[i] * COEFFS[ar, j];
|
||||
@ -979,6 +1002,14 @@ begin
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TCalculatedChartSource.EffectiveAccumulationRange: Cardinal;
|
||||
begin
|
||||
if (AccumulationMethod = camDerivative) and (AccumulationRange = 0) then
|
||||
Result := MAX_DERIVATIVE_RANGE
|
||||
else
|
||||
Result := AccumulationRange;
|
||||
end;
|
||||
|
||||
procedure TCalculatedChartSource.ExtractItem(
|
||||
out AItem: TChartDataItem; AIndex: Integer);
|
||||
var
|
||||
@ -1020,6 +1051,27 @@ begin
|
||||
Result := false;
|
||||
end;
|
||||
|
||||
procedure TCalculatedChartSource.RangeAround(
|
||||
AIndex: Integer; out ALeft, ARight: Integer);
|
||||
var
|
||||
ar: Integer;
|
||||
begin
|
||||
ar := EffectiveAccumulationRange;
|
||||
ar := IfThen(ar = 0, MaxInt div 2, ar - 1);
|
||||
ALeft := AIndex - IfThen(AccumulationDirection = cadForward, 0, ar);
|
||||
ARight := AIndex + IfThen(AccumulationDirection = cadBackward, 0, ar);
|
||||
ALeft := EnsureRange(ALeft, 0, Count - 1);
|
||||
ARight := EnsureRange(ARight, 0, Count - 1);
|
||||
end;
|
||||
|
||||
procedure TCalculatedChartSource.SetAccumulationDirection(
|
||||
AValue: TChartAccumulationDirection);
|
||||
begin
|
||||
if FAccumulationDirection = AValue then exit;
|
||||
FAccumulationDirection := AValue;
|
||||
Changed(nil);
|
||||
end;
|
||||
|
||||
procedure TCalculatedChartSource.SetAccumulationMethod(
|
||||
AValue: TChartAccumulationMethod);
|
||||
begin
|
||||
|
||||
@ -84,12 +84,19 @@ begin
|
||||
AssertEquals(2, FSource[1]^.X);
|
||||
AssertEquals(102 + 202, FSource[1]^.Y);
|
||||
AssertEquals(202 + 302, FSource[2]^.Y);
|
||||
FSource.AccumulationDirection := cadForward;
|
||||
AssertEquals(202 + 302, FSource[1]^.Y);
|
||||
AssertEquals(302 + 402, FSource[2]^.Y);
|
||||
FSource.AccumulationDirection := cadBackward;
|
||||
FSource.AccumulationMethod := camAverage;
|
||||
AssertEquals((2002 + 2102) / 2, FSource[20]^.Y);
|
||||
AssertEquals(1, FSource[0]^.X);
|
||||
AssertEquals(102, FSource[0]^.Y);
|
||||
AssertEquals((102 + 202) / 2, FSource[1]^.Y);
|
||||
AssertEquals(102, FSource[0]^.Y);
|
||||
FSource.AccumulationDirection := cadCenter;
|
||||
AssertEquals((1102 + 1202 + 1302) / 3, FSource[11]^.Y);
|
||||
FSource.AccumulationDirection := cadBackward;
|
||||
|
||||
FSource.AccumulationRange := 5;
|
||||
rng := TMWCRandomGenerator.Create;
|
||||
|
||||
@ -67,12 +67,23 @@
|
||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
<Parsing>
|
||||
<SyntaxOptions>
|
||||
<IncludeAssertionCode Value="True"/>
|
||||
</SyntaxOptions>
|
||||
</Parsing>
|
||||
<CodeGeneration>
|
||||
<Checks>
|
||||
<RangeChecks Value="True"/>
|
||||
<OverflowChecks Value="True"/>
|
||||
</Checks>
|
||||
</CodeGeneration>
|
||||
<Linking>
|
||||
<Debugging>
|
||||
<GenerateDebugInfo Value="True"/>
|
||||
<DebugInfoType Value="dsAuto"/>
|
||||
</Debugging>
|
||||
</Linking>
|
||||
<Other>
|
||||
<CompilerMessages>
|
||||
<UseMsgFile Value="True"/>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user