mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-09-22 13:59:16 +02:00
TAChart: Add cubic spline series
git-svn-id: trunk@31407 -
This commit is contained in:
parent
86b18e0c0a
commit
8697a41ace
@ -23,7 +23,7 @@ unit TAFuncSeries;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, Graphics,
|
Classes, Graphics, typ,
|
||||||
TAChartUtils, TACustomSeries, TACustomSource, TADrawUtils, TALegend, TATypes;
|
TAChartUtils, TACustomSeries, TACustomSource, TADrawUtils, TALegend, TATypes;
|
||||||
|
|
||||||
const
|
const
|
||||||
@ -132,6 +132,44 @@ type
|
|||||||
read FStep write SetStep default DEF_SPLINE_STEP;
|
read FStep write SetStep default DEF_SPLINE_STEP;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TCubicSplineSeries }
|
||||||
|
|
||||||
|
TCubicSplineSeries = class(TBasicPointSeries)
|
||||||
|
private
|
||||||
|
FPen: TChartPen;
|
||||||
|
FStep: TFuncSeriesStep;
|
||||||
|
|
||||||
|
procedure SetPen(AValue: TChartPen);
|
||||||
|
procedure SetStep(AValue: TFuncSeriesStep);
|
||||||
|
private
|
||||||
|
FX, FY, FCoeff: array of ArbFloat;
|
||||||
|
|
||||||
|
procedure PrepareCoeffs;
|
||||||
|
function Calculate(AX: Double): Double;
|
||||||
|
protected
|
||||||
|
procedure GetLegendItems(AItems: TChartLegendItems); override;
|
||||||
|
procedure SourceChanged(ASender: TObject); override;
|
||||||
|
|
||||||
|
public
|
||||||
|
procedure Assign(ASource: TPersistent); override;
|
||||||
|
constructor Create(AOwner: TComponent); override;
|
||||||
|
destructor Destroy; override;
|
||||||
|
|
||||||
|
procedure Draw(ADrawer: IChartDrawer); override;
|
||||||
|
published
|
||||||
|
property Active default true;
|
||||||
|
property AxisIndexX;
|
||||||
|
property AxisIndexY;
|
||||||
|
property ShowInLegend;
|
||||||
|
property Source;
|
||||||
|
property Title;
|
||||||
|
property ZPosition;
|
||||||
|
published
|
||||||
|
property Pen: TChartPen read FPen write SetPen;
|
||||||
|
property Step: TFuncSeriesStep
|
||||||
|
read FStep write SetStep default DEF_SPLINE_STEP;
|
||||||
|
end;
|
||||||
|
|
||||||
TFuncCalculate3DEvent =
|
TFuncCalculate3DEvent =
|
||||||
procedure (const AX, AY: Double; out AZ: Double) of object;
|
procedure (const AX, AY: Double; out AZ: Double) of object;
|
||||||
|
|
||||||
@ -183,7 +221,7 @@ type
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Math, SysUtils, TAGeometry, TAGraph;
|
ipf, Math, SysUtils, TAGeometry, TAGraph;
|
||||||
|
|
||||||
function DoublePointRotated(AX, AY: Double): TDoublePoint;
|
function DoublePointRotated(AX, AY: Double): TDoublePoint;
|
||||||
begin
|
begin
|
||||||
@ -277,21 +315,27 @@ begin
|
|||||||
OnCalculate(AX, Result)
|
OnCalculate(AX, Result)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TFuncSeries.Draw(ADrawer: IChartDrawer);
|
|
||||||
type
|
type
|
||||||
TTransform = function (A: Double): Double of object;
|
TTransform = function (A: Double): Double of object;
|
||||||
|
TCustomSeriesCrack = class(TCustomChartSeries);
|
||||||
|
|
||||||
|
procedure DrawFunction(
|
||||||
|
ADrawer: IChartDrawer; ASeries: TCustomChartSeries;
|
||||||
|
ADomainExclusions: TIntervalList; ACalc: TTransform; AStep: Integer);
|
||||||
|
type
|
||||||
TMakeDoublePoint = function (AX, AY: Double): TDoublePoint;
|
TMakeDoublePoint = function (AX, AY: Double): TDoublePoint;
|
||||||
|
|
||||||
var
|
var
|
||||||
axisToGraphXr, axisToGraphYr, graphToAxisXr, calc: TTransform;
|
axisToGraphXr, axisToGraphYr, graphToAxisXr: TTransform;
|
||||||
makeDP: TMakeDoublePoint;
|
makeDP: TMakeDoublePoint;
|
||||||
r: TDoubleRect = (coords:(NegInfinity, NegInfinity, Infinity, Infinity));
|
r: TDoubleRect = (coords:(NegInfinity, NegInfinity, Infinity, Infinity));
|
||||||
prev: TDoublePoint;
|
prev: TDoublePoint;
|
||||||
prevInExtent: Boolean;
|
prevInExtent: Boolean;
|
||||||
|
chart: TChart;
|
||||||
|
|
||||||
procedure CalcAt(AXg, AXa: Double; out APt: TDoublePoint; out AIn: Boolean);
|
procedure CalcAt(AXg, AXa: Double; out APt: TDoublePoint; out AIn: Boolean);
|
||||||
begin
|
begin
|
||||||
APt := makeDP(AXg, axisToGraphYr(calc(AXa)));
|
APt := makeDP(AXg, axisToGraphYr(ACalc(AXa)));
|
||||||
AIn := (r.a <= APt) and (APt <= r.b);
|
AIn := (r.a <= APt) and (APt <= r.b);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -299,7 +343,7 @@ var
|
|||||||
begin
|
begin
|
||||||
CalcAt(AXg, AXa, prev, prevInExtent);
|
CalcAt(AXg, AXa, prev, prevInExtent);
|
||||||
if prevInExtent then
|
if prevInExtent then
|
||||||
ADrawer.MoveTo(FChart.GraphToImage(prev));
|
ADrawer.MoveTo(chart.GraphToImage(prev));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure LineTo(AXg, AXa: Double);
|
procedure LineTo(AXg, AXa: Double);
|
||||||
@ -310,10 +354,10 @@ var
|
|||||||
CalcAt(AXg, AXa, p, inExtent);
|
CalcAt(AXg, AXa, p, inExtent);
|
||||||
t := p;
|
t := p;
|
||||||
if inExtent and prevInExtent then
|
if inExtent and prevInExtent then
|
||||||
ADrawer.LineTo(FChart.GraphToImage(p))
|
ADrawer.LineTo(chart.GraphToImage(p))
|
||||||
else if LineIntersectsRect(prev, t, r) then begin
|
else if LineIntersectsRect(prev, t, r) then begin
|
||||||
ADrawer.MoveTo(FChart.GraphToImage(prev));
|
ADrawer.MoveTo(chart.GraphToImage(prev));
|
||||||
ADrawer.LineTo(FChart.GraphToImage(t));
|
ADrawer.LineTo(chart.GraphToImage(t));
|
||||||
end;
|
end;
|
||||||
prevInExtent := inExtent;
|
prevInExtent := inExtent;
|
||||||
prev := p;
|
prev := p;
|
||||||
@ -323,46 +367,41 @@ var
|
|||||||
hint: Integer;
|
hint: Integer;
|
||||||
xg, xa, xg1, xa1, xmax, graphStep: Double;
|
xg, xa, xg1, xa1, xmax, graphStep: Double;
|
||||||
begin
|
begin
|
||||||
if Assigned(OnCalculate) then
|
chart := TCustomSeriesCrack(ASeries).FChart;
|
||||||
calc := @DoCalculate
|
TCustomSeriesCrack(ASeries).GetGraphBounds(r);
|
||||||
else if csDesigning in ComponentState then
|
RectIntersectsRect(r, chart.CurrentExtent);
|
||||||
calc := @DoCalcIdentity
|
|
||||||
else
|
|
||||||
exit;
|
|
||||||
GetGraphBounds(r);
|
|
||||||
RectIntersectsRect(r, FChart.CurrentExtent);
|
|
||||||
|
|
||||||
if IsRotated then begin
|
with TCustomSeriesCrack(ASeries) do
|
||||||
axisToGraphXr := @AxisToGraphY;
|
if IsRotated then begin
|
||||||
axisToGraphYr := @AxisToGraphX;
|
axisToGraphXr := @AxisToGraphY;
|
||||||
graphToAxisXr := @GraphToAxisY;
|
axisToGraphYr := @AxisToGraphX;
|
||||||
makeDP := @DoublePointRotated;
|
graphToAxisXr := @GraphToAxisY;
|
||||||
graphStep := FChart.YImageToGraph(-Step) - FChart.YImageToGraph(0);
|
makeDP := @DoublePointRotated;
|
||||||
xg := r.a.Y;
|
graphStep := chart.YImageToGraph(-AStep) - chart.YImageToGraph(0);
|
||||||
xmax := r.b.Y;
|
xg := r.a.Y;
|
||||||
end
|
xmax := r.b.Y;
|
||||||
else begin
|
end
|
||||||
axisToGraphXr := @AxisToGraphX;
|
else begin
|
||||||
axisToGraphYr := @AxisToGraphY;
|
axisToGraphXr := @AxisToGraphX;
|
||||||
graphToAxisXr := @GraphToAxisX;
|
axisToGraphYr := @AxisToGraphY;
|
||||||
makeDP := @DoublePoint;
|
graphToAxisXr := @GraphToAxisX;
|
||||||
graphStep := FChart.XImageToGraph(Step) - FChart.XImageToGraph(0);
|
makeDP := @DoublePoint;
|
||||||
xg := r.a.X;
|
graphStep := chart.XImageToGraph(AStep) - chart.XImageToGraph(0);
|
||||||
xmax := r.b.X;
|
xg := r.a.X;
|
||||||
end;
|
xmax := r.b.X;
|
||||||
|
end;
|
||||||
|
|
||||||
hint := 0;
|
hint := 0;
|
||||||
xa := graphToAxisXr(xg);
|
xa := graphToAxisXr(xg);
|
||||||
if DomainExclusions.Intersect(xa, xa, hint) then
|
if ADomainExclusions.Intersect(xa, xa, hint) then
|
||||||
xg := axisToGraphXr(xa);
|
xg := axisToGraphXr(xa);
|
||||||
|
|
||||||
MoveTo(xg, xa);
|
MoveTo(xg, xa);
|
||||||
|
|
||||||
ADrawer.Pen := Pen;
|
|
||||||
while xg < xmax do begin
|
while xg < xmax do begin
|
||||||
xg1 := xg + graphStep;
|
xg1 := xg + graphStep;
|
||||||
xa1 := graphToAxisXr(xg1);
|
xa1 := graphToAxisXr(xg1);
|
||||||
if DomainExclusions.Intersect(xa, xa1, hint) then begin
|
if ADomainExclusions.Intersect(xa, xa1, hint) then begin
|
||||||
LineTo(axisToGraphXr(xa), xa);
|
LineTo(axisToGraphXr(xa), xa);
|
||||||
xg1 := axisToGraphXr(xa1);
|
xg1 := axisToGraphXr(xa1);
|
||||||
MoveTo(xg1, xa1);
|
MoveTo(xg1, xa1);
|
||||||
@ -374,6 +413,22 @@ begin
|
|||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
procedure TFuncSeries.Draw(ADrawer: IChartDrawer);
|
||||||
|
var
|
||||||
|
calc: TTransform;
|
||||||
|
begin
|
||||||
|
if Assigned(OnCalculate) then
|
||||||
|
calc := @DoCalculate
|
||||||
|
else if csDesigning in ComponentState then
|
||||||
|
calc := @DoCalcIdentity
|
||||||
|
else
|
||||||
|
exit;
|
||||||
|
ADrawer.Pen := Pen;
|
||||||
|
DrawFunction(
|
||||||
|
ADrawer, TCustomSeriesCrack(TCustomChartSeries(Self)),
|
||||||
|
DomainExclusions, calc, Step);
|
||||||
|
end;
|
||||||
|
|
||||||
procedure TFuncSeries.GetLegendItems(AItems: TChartLegendItems);
|
procedure TFuncSeries.GetLegendItems(AItems: TChartLegendItems);
|
||||||
begin
|
begin
|
||||||
AItems.Add(TLegendItemLine.Create(Pen, Title));
|
AItems.Add(TLegendItemLine.Create(Pen, Title));
|
||||||
@ -533,6 +588,98 @@ begin
|
|||||||
UpdateParentChart;
|
UpdateParentChart;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ TCubicSplineSeries }
|
||||||
|
|
||||||
|
procedure TCubicSplineSeries.Assign(ASource: TPersistent);
|
||||||
|
begin
|
||||||
|
if ASource is TCubicSplineSeries then
|
||||||
|
with TCubicSplineSeries(ASource) do begin
|
||||||
|
Self.Pen := FPen;
|
||||||
|
Self.FStep := FStep;
|
||||||
|
end;
|
||||||
|
inherited Assign(ASource);
|
||||||
|
end;
|
||||||
|
|
||||||
|
function TCubicSplineSeries.Calculate(AX: Double): Double;
|
||||||
|
var
|
||||||
|
ok: Integer;
|
||||||
|
begin
|
||||||
|
Result := ipfspn(High(FCoeff) - 1, FX[0], FY[0], FCoeff[0], AX, ok);
|
||||||
|
end;
|
||||||
|
|
||||||
|
constructor TCubicSplineSeries.Create(AOwner: TComponent);
|
||||||
|
begin
|
||||||
|
inherited Create(AOwner);
|
||||||
|
FPen := TChartPen.Create;
|
||||||
|
FPen.OnChange := @StyleChanged;
|
||||||
|
FStep := DEF_SPLINE_STEP;
|
||||||
|
end;
|
||||||
|
|
||||||
|
destructor TCubicSplineSeries.Destroy;
|
||||||
|
begin
|
||||||
|
FreeAndNil(FPen);
|
||||||
|
inherited;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCubicSplineSeries.Draw(ADrawer: IChartDrawer);
|
||||||
|
var
|
||||||
|
de: TIntervalList;
|
||||||
|
begin
|
||||||
|
if FCoeff = nil then
|
||||||
|
PrepareCoeffs;
|
||||||
|
if FCoeff = nil then exit;
|
||||||
|
de := TIntervalList.Create;
|
||||||
|
try
|
||||||
|
ADrawer.Pen := Pen;
|
||||||
|
DrawFunction(ADrawer, Self, de, @Calculate, Step);
|
||||||
|
finally
|
||||||
|
de.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCubicSplineSeries.GetLegendItems(AItems: TChartLegendItems);
|
||||||
|
begin
|
||||||
|
AItems.Add(TLegendItemLine.Create(Pen, Title));
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCubicSplineSeries.PrepareCoeffs;
|
||||||
|
var
|
||||||
|
i, n: Integer;
|
||||||
|
begin
|
||||||
|
n := Source.Count;
|
||||||
|
SetLength(FX, n);
|
||||||
|
SetLength(FY, n);
|
||||||
|
SetLength(FCoeff, n);
|
||||||
|
for i := 0 to n - 1 do
|
||||||
|
with Source[i]^ do begin
|
||||||
|
FX[i] := X;
|
||||||
|
FY[i] := Y;
|
||||||
|
end;
|
||||||
|
ipfisn(n - 2, FX[0], FY[0], FCoeff[0], i);
|
||||||
|
if i > 1 then
|
||||||
|
FCoeff := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCubicSplineSeries.SetPen(AValue: TChartPen);
|
||||||
|
begin
|
||||||
|
if FPen = AValue then exit;
|
||||||
|
FPen.Assign(AValue);
|
||||||
|
UpdateParentChart;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCubicSplineSeries.SetStep(AValue: TFuncSeriesStep);
|
||||||
|
begin
|
||||||
|
if FStep = AValue then exit;
|
||||||
|
FStep := AValue;
|
||||||
|
UpdateParentChart;
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TCubicSplineSeries.SourceChanged(ASender: TObject);
|
||||||
|
begin
|
||||||
|
inherited SourceChanged(ASender);
|
||||||
|
FCoeff := nil;
|
||||||
|
end;
|
||||||
|
|
||||||
{ TColorMapSeries }
|
{ TColorMapSeries }
|
||||||
|
|
||||||
procedure TColorMapSeries.Assign(ASource: TPersistent);
|
procedure TColorMapSeries.Assign(ASource: TPersistent);
|
||||||
@ -736,6 +883,7 @@ end;
|
|||||||
initialization
|
initialization
|
||||||
RegisterSeriesClass(TFuncSeries, 'Function series');
|
RegisterSeriesClass(TFuncSeries, 'Function series');
|
||||||
RegisterSeriesClass(TBSplineSeries, 'B-Spline series');
|
RegisterSeriesClass(TBSplineSeries, 'B-Spline series');
|
||||||
|
RegisterSeriesClass(TCubicSplineSeries, 'Cubic spline series');
|
||||||
RegisterSeriesClass(TColorMapSeries, 'Color map series');
|
RegisterSeriesClass(TColorMapSeries, 'Color map series');
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
Loading…
Reference in New Issue
Block a user