TAChart: Add smoothed derivative source. Update demo.

git-svn-id: trunk@32412 -
This commit is contained in:
ask 2011-09-18 21:35:15 +00:00
parent 0271730391
commit 979e916f7b
3 changed files with 67 additions and 23 deletions

View File

@ -329,6 +329,15 @@ object Form1: TForm1
TabOrder = 1
Text = 'Backward'
end
object cbSmooth: TCheckBox
Left = 332
Height = 17
Top = 5
Width = 56
Caption = 'Smooth'
OnChange = cbSmoothChange
TabOrder = 2
end
end
end
end

View File

@ -34,6 +34,7 @@ type
chCalcLineSeriesAvg: TLineSeries;
chCalcLineSeriesSum: TLineSeries;
cbAccDirStatistics: TComboBox;
cbSmooth: TCheckBox;
seAccumulationRange: TSpinEdit;
lblAccumulationRange: TLabel;
ListChartSource1: TListChartSource;
@ -52,6 +53,7 @@ type
procedure cbAccDirDerivativeChange(Sender: TObject);
procedure cbAccDirStatisticsChange(Sender: TObject);
procedure cbCumulativeChange(Sender: TObject);
procedure cbSmoothChange(Sender: TObject);
procedure CreateData;
procedure seAccumulationRangeChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
@ -88,6 +90,14 @@ begin
chCalcLineSeriesSum.Active := cbCumulative.Checked;
end;
procedure TForm1.cbSmoothChange(Sender: TObject);
begin
if cbSmooth.Checked then
ccsDerivative.AccumulationMethod := camSmoothDerivative
else
ccsDerivative.AccumulationMethod := camDerivative;
end;
procedure TForm1.CreateData;
const
N = 100;

View File

@ -157,7 +157,8 @@ type
property Sorted: Boolean read FSorted write FSorted default false;
end;
TChartAccumulationMethod = (camNone, camSum, camAverage, camDerivative);
TChartAccumulationMethod = (
camNone, camSum, camAverage, camDerivative, camSmoothDerivative);
TChartAccumulationDirection = (cadBackward, cadForward, cadCenter);
{ TCalculatedChartSource }
@ -183,6 +184,7 @@ type
procedure Changed(ASender: TObject);
function EffectiveAccumulationRange: Cardinal;
procedure ExtractItem(AIndex: Integer);
function IsDerivative: Boolean; inline;
procedure RangeAround(AIndex: Integer; out ALeft, ARight: Integer);
procedure SetAccumulationDirection(AValue: TChartAccumulationDirection);
procedure SetAccumulationMethod(AValue: TChartAccumulationMethod);
@ -915,14 +917,12 @@ begin
FHistory.GetSum(FItem);
FItem.MultiplyY(1 / (newRight - newLeft + 1));
end;
camDerivative:
camDerivative, camSmoothDerivative:
CalcDerivative(AIndex);
end;
FIndex := AIndex;
end;
// Derivative is approximated by finite differences
// with accuracy order of (AccumulationRange - 1).
procedure TCalculatedChartSource.CalcDerivative(AIndex: Integer);
procedure WeightedSum(const ACoeffs: array of Double; ADir, ACount: Integer);
@ -938,21 +938,40 @@ procedure TCalculatedChartSource.CalcDerivative(AIndex: Integer);
end;
end;
// Derivative is approximated by finite differences
// with accuracy order of (AccumulationRange - 1).
// Smoothed derivative coefficients are based on work
// by Pavel Holoborodko (http://www.holoborodko.com/pavel/).
const
COEFFS_BF: array [2..7, 0..6] of Double = (
( -1, 1, 0, 0, 0, 0, 0),
( -3/2, 2, -1/2, 0, 0, 0, 0),
( -11/6, 3, -3/2, 1/3, 0, 0, 0),
( -25/12, 4, -3, 4/3, -1/4, 0, 0),
(-137/60, 5, -5, 10/3, -5/4, 1/5, 0),
( -49/20, 6, -15/2, 20/3, -15/4, 6/5, -1/6));
COEFFS_C: array [2..5, 0..4] of Double = (
(0, 1/2, 0, 0, 0),
(0, 2/3, -1/12, 0, 0),
(0, 3/4, -3/20, 1/60, 0),
(0, 4/5, -1/5, 4/105, -1/280));
COEFFS_BF: array [Boolean, 2..7, 0..6] of Double = (
( ( -1, 1, 0, 0, 0, 0, 0),
( -3/2, 2, -1/2, 0, 0, 0, 0),
( -11/6, 3, -3/2, 1/3, 0, 0, 0),
( -25/12, 4, -3, 4/3, -1/4, 0, 0),
(-137/60, 5, -5, 10/3, -5/4, 1/5, 0),
( -49/20, 6, -15/2, 20/3, -15/4, 6/5, -1/6)
),
( ( -1, 1, 0, 0, 0, 0, 0),
( -1/2, 0, 1/2, 0, 0, 0, 0),
( -1/4, -1/4, 1/4, 1/4, 0, 0, 0),
( -1/8, -1/4, 0, 1/4, 1/8, 0, 0),
(-1/16, -3/16, -1/8, 1/8, 3/16, 1/16, 0),
(-1/32, -1/8, -5/32, 0, 5/32, 1/8, 1/32)
));
COEFFS_C: array [Boolean, 2..5, 0..4] of Double = (
( (0, 1/2, 0, 0, 0),
(0, 2/3, -1/12, 0, 0),
(0, 3/4, -3/20, 1/60, 0),
(0, 4/5, -1/5, 4/105, -1/280)
),
( (0, 1/2, 0, 0, 0),
(0, 1/4, 1/8, 0, 0),
(0, 5/32, 1/8, 1/32, 0),
(0, 7/64, 7/64, 3/64, 1/128)
));
var
ar, iLeft, iRight, dir: Integer;
isSmooth: Boolean;
dx: Double;
begin
RangeAround(AIndex, iLeft, iRight);
@ -961,17 +980,17 @@ begin
dx := Max(
FItem.X - FHistory.GetPtr(AIndex - iLeft - 1)^.X,
FHistory.GetPtr(AIndex - iLeft + 1)^.X - FItem.X);
ar := Min(Min(AIndex - iLeft, iRight - AIndex) + 1, High(COEFFS_C));
ar := Min(Min(AIndex - iLeft, iRight - AIndex) + 1, High(COEFFS_C[false]));
dir := 0;
end;
cotFirst: begin
dx := FHistory.GetPtr(1)^.X - FItem.X;
ar := Min(iRight - AIndex + 1, High(COEFFS_C));
ar := Min(iRight - AIndex + 1, High(COEFFS_BF[false]));
dir := 1;
end;
cotSecond: begin
dx := FItem.X - FHistory.GetPtr(AIndex - iLeft - 1)^.X;
ar := Min(AIndex - iLeft + 1, High(COEFFS_C));
ar := Min(AIndex - iLeft + 1, High(COEFFS_BF[false]));
dir := -1;
end;
cotBoth: begin
@ -985,12 +1004,13 @@ begin
end;
FItem.SetY(0.0);
AIndex -= iLeft;
isSmooth := AccumulationMethod = camSmoothDerivative;
if dir = 0 then begin
WeightedSum(COEFFS_C[ar], -1, ar);
WeightedSum(COEFFS_C[ar], +1, ar);
WeightedSum(COEFFS_C[isSmooth][ar], -1, ar);
WeightedSum(COEFFS_C[isSmooth][ar], +1, ar);
end
else
WeightedSum(COEFFS_BF[ar], dir, ar);
WeightedSum(COEFFS_BF[isSmooth][ar], dir, ar);
FItem.MultiplyY(1 / dx);
end;
@ -1038,7 +1058,7 @@ function TCalculatedChartSource.EffectiveAccumulationRange: Cardinal;
const
MAX_DERIVATIVE_RANGE = 10;
begin
if (AccumulationMethod = camDerivative) and (AccumulationRange = 0) then
if IsDerivative and (AccumulationRange = 0) then
Result := MAX_DERIVATIVE_RANGE
else
Result := AccumulationRange;
@ -1076,6 +1096,11 @@ begin
CalcPercentage;
end;
function TCalculatedChartSource.IsDerivative: Boolean;
begin
Result := AccumulationMethod in [camDerivative, camSmoothDerivative];
end;
function TCalculatedChartSource.IsSorted: Boolean;
begin
if Origin <> nil then