home *** CD-ROM | disk | FTP | other *** search
/ Delphi 5 for Professionals / DELPHI5.iso / AddOns / Components / TEECHART / Src Code / CURVFITT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1998-10-24  |  11.1 KB  |  377 lines

  1. {****************************************************}
  2. {   TCurveFittingFunction                            }
  3. {   Copyright (c) 1995-1998 by David Berneda         }
  4. {****************************************************}
  5. {$I teedefs.inc}
  6. unit CurvFitt;
  7.  
  8. interface
  9.  
  10. { This unit shows how a new Chart Series component can be easily created.
  11.   TCustomFittingFunction derives from standard TTeeFunction.
  12.  
  13.   TCurveFittingFunction and TTrendFunction both derive from
  14.   TCustomFittingFunction.
  15.  
  16.   Based on a Polynomial degree value (# of polynomy items), a curve
  17.   fitting is calculated for each X,Y pair value to determine the new
  18.   Y position for each source X value.
  19. }
  20.  
  21. Uses Classes, TeePoly, StatChar, Teengine;
  22.  
  23. Type
  24.   TTypeFitting=( cfPolynomial
  25.                  {$IFDEF TEEOCX}
  26.                  ,cfLogarithmic
  27.                  ,cfExponential
  28.                  {$ENDIF}
  29.                 );
  30.  
  31.   TCustomFittingFunction=class(TTeeFunction)
  32.   private
  33.     FPolyDegree     : Integer; { <-- between 1 and 20 }
  34.     FTypeFitting    : TTypeFitting;
  35.     FFirstPoint     : Longint;
  36.     FLastPoint      : Longint;
  37.     FFirstCalcPoint : Longint;
  38.     FLastCalcPoint  : Longint;
  39.     { internal }
  40.     IAnswerVector   : TDegreeVector;
  41.     IMinYValue      : Double;
  42.     Procedure SetPolyDegree(Value:Integer);
  43.     Procedure SetTypeFitting(Value:TTypeFitting);
  44.     Procedure SetFirstPoint(Value:Longint);
  45.     Procedure SetLastPoint(Value:Longint);
  46.     Procedure SetFirstCalcPoint(Value:Longint);
  47.     Procedure SetLastCalcPoint(Value:Longint);
  48.   protected
  49.     Function GetAnswerVector(Index:Integer):Double;
  50.     Procedure SetLongintProperty(Var Variable:Longint; Value:Longint);
  51.     procedure AddFittedPoints(Source:TChartSeries); virtual;
  52.   public
  53.     Constructor Create(AOwner: TComponent); override;
  54.     procedure AddPoints(Source:TChartSeries); override;
  55.     { new }
  56.     Function GetCurveYValue(Source:TChartSeries; Const X:Double):Double;
  57.     property AnswerVector[Index:Integer]:Double read GetAnswerVector;
  58.     property PolyDegree:Integer read FPolyDegree write SetPolyDegree default 5;
  59.     property TypeFitting:TTypeFitting read FTypeFitting write SetTypeFitting default cfPolynomial;
  60.     property FirstPoint:Longint read FFirstPoint write SetFirstPoint default -1;
  61.     property LastPoint:Longint read FLastPoint write SetLastPoint default -1;
  62.     property FirstCalcPoint:Longint read FFirstCalcPoint write SetFirstCalcPoint default -1;
  63.     property LastCalcPoint:Longint read FLastCalcPoint write SetLastCalcPoint default -1;
  64.   end;
  65.  
  66.   TCurveFittingFunction=class(TCustomFittingFunction)
  67.   published
  68.     property PolyDegree;
  69.     property TypeFitting;
  70.     property FirstPoint;
  71.     property LastPoint;
  72.     property FirstCalcPoint;
  73.     property LastCalcPoint;
  74.   end;
  75.  
  76.   TTrendFunction=class(TTeeFunction)
  77.   protected
  78.     Procedure CalculatePeriod( Source:TChartSeries;
  79.                                Const tmpX:Double;
  80.                                FirstIndex,LastIndex:Longint); override;
  81.     Procedure CalculateAllPoints( Source:TChartSeries;
  82.                                   NotMandatorySource:TChartValueList); override;
  83.   public
  84.     Function Calculate(SourceSeries:TChartSeries; First,Last:Longint):Double; override;
  85.     Function CalculateMany(SourceSeriesList:TList; ValueIndex:Longint):Double;  override;
  86.     Procedure CalculateTrend( Var m,b:Double; Source:TChartSeries;
  87.                               FirstIndex,LastIndex:Longint);
  88.   end;
  89.  
  90. implementation
  91.  
  92. Uses SysUtils,TeeProCo,Chart,TeeProcs,TeeConst,TeCanvas;
  93.  
  94. { TCurveFittingFunction }
  95. Constructor TCustomFittingFunction.Create(AOwner: TComponent);
  96. Begin
  97.   inherited Create(AOwner);
  98.   CanUsePeriod:=False;
  99.   InternalSetPeriod(1);
  100.   FPolyDegree:=5;
  101.   FTypeFitting:=cfPolynomial;
  102.   FFirstPoint:=-1;
  103.   FLastPoint:=-1;
  104.   FFirstCalcPoint:=-1;
  105.   FLastCalcPoint:=-1;
  106.   {$IFDEF TEETRIAL}
  107.   TeeTrial(ComponentState);
  108.   {$ENDIF}
  109. end;
  110.  
  111. Procedure TCustomFittingFunction.SetLongintProperty(Var Variable:Longint; Value:Longint);
  112. Begin
  113.   if Variable<>Value then
  114.   Begin
  115.     Variable:=Value;
  116.     Recalculate;
  117.   end;
  118. end;
  119.  
  120. Procedure TCustomFittingFunction.SetFirstPoint(Value:Longint);
  121. Begin
  122.   SetLongintProperty(FFirstPoint,Value);
  123. End;
  124.  
  125. Procedure TCustomFittingFunction.SetLastPoint(Value:Longint);
  126. Begin
  127.   SetLongintProperty(FLastPoint,Value);
  128. End;
  129.  
  130. Procedure TCustomFittingFunction.SetFirstCalcPoint(Value:Longint);
  131. Begin
  132.   SetLongintProperty(FFirstCalcPoint,Value);
  133. End;
  134.  
  135. Procedure TCustomFittingFunction.SetLastCalcPoint(Value:Longint);
  136. Begin
  137.   SetLongintProperty(FLastCalcPoint,Value);
  138. End;
  139.  
  140. Procedure TCustomFittingFunction.SetTypeFitting(Value:TTypeFitting);
  141. Begin
  142.   if FTypeFitting<>Value then
  143.   Begin
  144.     FTypeFitting:=Value;
  145.     Recalculate;
  146.   end;
  147. end;
  148.  
  149. Procedure TCustomFittingFunction.SetPolyDegree(Value:Integer);
  150. Begin
  151.   if FPolyDegree<>Value then
  152.   begin
  153.     if (Value<1) or (Value>20) then
  154.        Raise Exception.Create(TeeMsg_PolyDegreeRange);
  155.     FPolyDegree:=Value;
  156.     Recalculate;
  157.   end;
  158. end;
  159.  
  160. Function TCustomFittingFunction.GetAnswerVector(Index:Integer):Double;
  161. Begin
  162.   if (Index<1) or (Index>FPolyDegree) then
  163.      Raise Exception.CreateFmt(TeeMsg_AnswerVectorIndex,[FPolyDegree]);
  164.   result:=IAnswerVector[Index];
  165. End;
  166.  
  167. procedure TCustomFittingFunction.AddFittedPoints(Source:TChartSeries);
  168. Var tmpX         : Double;
  169.     tmpMinXValue : Double;
  170.     t            : Longint;
  171.     tmpStart     : Longint;
  172.     tmpEnd       : Longint;
  173.     AList        : TChartValueList;
  174. begin
  175.   AList:=ValueList(Source);
  176.   With Source do
  177.   begin
  178.     tmpMinXValue:=XValues.MinValue;
  179.     IMinYValue:=AList.MinValue;
  180.     if FFirstPoint=-1 then tmpStart:=0
  181.                       else tmpStart:=FFirstPoint;
  182.     if FLastPoint=-1 then tmpEnd:=Count-1
  183.                      else tmpEnd:=FLastPoint;
  184.     for t:=tmpStart to tmpEnd do  { 1 to 1 relationship between source and self }
  185.     begin
  186.       tmpX:=XValue[t];
  187.       ParentSeries.AddXY( tmpX, CalcFitting( FPolyDegree,
  188.                                              IAnswerVector,
  189.                                              tmpX-tmpMinXValue)+IMinYValue
  190.                                              {$IFNDEF D4},'', clTeeColor{$ENDIF});
  191.     end;
  192.   end;
  193. end;
  194.  
  195. procedure TCustomFittingFunction.AddPoints(Source:TChartSeries);
  196. var t            : Longint;
  197.     tmpStart     : Longint;
  198.     tmpEnd       : Longint;
  199.     tmpCount     : Longint;
  200.     tmpPos       : Longint;
  201.     IXVector     : PVector;
  202.     IYVector     : PVector;
  203.     tmpMinXValue : Double;
  204.     AList        : TChartValueList;
  205. Begin
  206.   ParentSeries.Clear;
  207.   With Source do
  208.   if Count>=FPolyDegree then
  209.   begin
  210.     AList:=ValueList(Source);
  211.     New(IXVector);
  212.     try
  213.       New(IYVector);
  214.       try
  215.         tmpMinXValue:=XValues.MinValue;
  216.         IMinYValue:=AList.MinValue;
  217.         if FFirstCalcPoint=-1 then tmpStart:=0
  218.                               else tmpStart:=MaxLong(0,FFirstCalcPoint);
  219.         if FLastCalcPoint=-1 then tmpEnd:=Count-1
  220.                              else tmpEnd:=MinLong(Count-1,FLastCalcPoint);
  221.         tmpCount:=(tmpEnd-tmpStart+1);
  222.         for t:=1 to tmpCount do
  223.         Begin
  224.           tmpPos:=t+tmpStart-1;
  225.           IXVector^[t]:=New(PFloat);
  226.           PFloat(IXVector^[t])^:=XValue[tmpPos]-tmpMinXValue;
  227.           IYVector^[t]:=New(PFloat);
  228.           PFloat(IYVector^[t])^:=AList.Value[tmpPos]-IMinYValue;
  229.         end;
  230.         try
  231.           PolyFitting(tmpCount,FPolyDegree,IXVector,IYVector,IAnswerVector);
  232.           AddFittedPoints(Source);
  233.         finally
  234.           for t:=1 to tmpCount do
  235.           begin
  236.             Dispose(PFloat(IXVector^[t]));
  237.             Dispose(PFloat(IYVector^[t]));
  238.           end;
  239.         end;
  240.       finally
  241.         Dispose(IYVector);
  242.       end;
  243.     finally
  244.       Dispose(IXVector);
  245.     end;
  246.   end;
  247. end;
  248.  
  249. { calculates and returns the Y value corresponding to a X value }
  250. Function TCustomFittingFunction.GetCurveYValue(Source:TChartSeries; Const X:Double):Double;
  251. Begin
  252.   result:=CalcFitting(FPolyDegree,IAnswerVector,X-Source.XValues.MinValue)+IMinYValue;
  253. end;
  254.  
  255. { TTrendFunction }
  256. Function TTrendFunction.Calculate(SourceSeries:TChartSeries; First,Last:Longint):Double;
  257. begin
  258.   result:=0;
  259. end;
  260.  
  261. Function TTrendFunction.CalculateMany(SourceSeriesList:TList; ValueIndex:Longint):Double;
  262. begin
  263.   result:=0;
  264. end;
  265.  
  266. Procedure TTrendFunction.CalculateTrend(Var m,b:Double; Source:TChartSeries; FirstIndex,LastIndex:Longint);
  267. var n       : Integer;
  268.     t       : Integer;
  269.     x       : Double;
  270.     y       : Double;
  271.     Divisor : Double;
  272.     SumX    : Double;
  273.     SumXY   : Double;
  274.     SumY    : Double;
  275.     SumX2   : Double;
  276.     NotMandatory:TChartValueList;
  277. begin
  278.   if FirstIndex=TeeAllValues then n:=Source.Count
  279.                              else n:=LastIndex-FirstIndex+1;
  280.   if n>1 then
  281.   With Source do
  282.   begin
  283.     if YMandatory then NotMandatory:=XValues
  284.                   else NotMandatory:=YValues;
  285.     if FirstIndex=TeeAllValues then
  286.     begin
  287.       SumX:=NotMandatory.Total;
  288.       SumY:=ValueList(Source).Total;
  289.     end
  290.     else
  291.     begin
  292.       SumX:=0;
  293.       SumY:=0;
  294.     end;
  295.     SumX2:=0;
  296.     SumXY:=0;
  297.     With ValueList(Source) do
  298.     for t:=FirstIndex to LastIndex do
  299.     begin
  300.       x:=NotMandatory[t];
  301.       y:=Value[t];
  302.       SumXY:=SumXY+x*y;
  303.       SumX2:=SumX2+Sqr(x);
  304.       if FirstIndex<>TeeAllValues then
  305.       begin
  306.         SumX:=SumX+x;
  307.         SumY:=SumY+y;
  308.       end;
  309.     end;
  310.     Divisor:=n*SumX2-Sqr(SumX);
  311.     if Divisor<>0 then
  312.     begin
  313.       m:=( (n*SumXY) - (SumX*SumY) ) / Divisor;
  314.       b:=( (SumY*SumX2) - (SumX*SumXY) ) / Divisor;
  315.     end
  316.     else
  317.     begin
  318.       m:=1;
  319.       b:=0;
  320.     end;
  321.   end;
  322. end;
  323.  
  324. Procedure TTrendFunction.CalculatePeriod( Source:TChartSeries;
  325.                                           Const tmpX:Double;
  326.                                           FirstIndex,LastIndex:Longint);
  327. Var m : Double;
  328.     b : Double;
  329.  
  330.   Procedure AddPoint(Const Value:Double);
  331.   begin
  332.     ParentSeries.AddXY( Value, m*Value+b {$IFNDEF D4},'', clTeeColor{$ENDIF} );
  333.   end;
  334.  
  335. Var n:Integer;
  336. begin
  337.   if FirstIndex=TeeAllValues then n:=Source.Count
  338.                              else n:=LastIndex-FirstIndex+1;
  339.   if n>1 then { minimum 2 points to calculate a trend }
  340.   begin
  341.     CalculateTrend(m,b,Source,FirstIndex,LastIndex);
  342.     With Source do
  343.     if YMandatory then
  344.     begin
  345.       AddPoint(XValues.Value[FirstIndex]);
  346.       AddPoint(XValues.Value[LastIndex]);
  347.     end
  348.     else
  349.     begin
  350.       AddPoint(YValues.Value[FirstIndex]);
  351.       AddPoint(YValues.Value[LastIndex]);
  352.     end;
  353.   end;
  354. end;
  355.  
  356. Procedure TTrendFunction.CalculateAllPoints( Source:TChartSeries;
  357.                                              NotMandatorySource:TChartValueList);
  358. begin
  359.   CalculatePeriod(Source,0,0,Source.Count-1);
  360. end;
  361.  
  362. Procedure TeeCurvFittExitProc; far;
  363. begin
  364.   UnRegisterTeeFunctions([ TCurveFittingFunction, TTrendFunction ]);
  365. end;
  366.  
  367. initialization
  368.   RegisterTeeBasicFunction( TCurveFittingFunction, TeeMsg_FunctionCurveFitting );
  369.   RegisterTeeFunction( TTrendFunction, TeeMsg_FunctionTrend, TeeMsg_GalleryFunctions,1 );
  370. {$IFDEF D1}
  371.   AddExitProc(TeeCurvFittExitProc);
  372. {$ELSE}
  373. finalization
  374.   TeeCurvFittExitProc;
  375. {$ENDIF}
  376. end.
  377.