Page 1 of 1

TChart Line Chart Series with DrawStyle Curve Dips Below Zero - How to Fix?

Posted: Wed Sep 06, 2023 6:24 am
by 16894501
I am facing an issue with the Line chart series of TChart when using the DrawStyle Curve. The line is going below 0, even though none of the values are below zero (please refer to the attached image). I am looking for a solution to prevent the line from dipping below zero and maintain the curve style. Any help would be greatly appreciated, and thank you in advance for your responses.

Re: TChart Line Chart Series with DrawStyle Curve Dips Below Zero - How to Fix?

Posted: Wed Sep 06, 2023 9:41 am
by yeray
Hello,

I'd say this is something to expect if you want the curve to pass through the points.
If you don't need the curve to pass though the points, you can use a TSmoothingFunction with Interpolate:=false as follows:

Code: Select all

uses Chart, Series, TeeSpline;

var Chart1: TChart;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Chart1:=TChart.Create(Self);

  with Chart1 do
  begin
    Parent:=Self;
    Align:=alClient;
    Color:=clWhite;
    Gradient.Visible:=False;
    Walls.Back.Color:=clWhite;
    Walls.Back.Gradient.Visible:=False;
    Legend.Hide;
    View3D:=False;

    Axes.Left.SetMinMax(-1, 4);
    Axes.Left.Increment:=1;
    Axes.Bottom.Grid.Hide;
  end;

  with TLineSeries(Chart1.AddSeries(TLineSeries)) do
  begin
    Pointer.Show;
    Pointer.Size:=2;
    DrawStyle:=dsCurve;

    Add(0);
    Add(0);
    Add(3);
    Add(0);
    Add(0);
    Add(2);
    Add(2);
    Add(0);
    Add(1);
    Add(0);
    Add(0);
  end;

  with TLineSeries(Chart1.AddSeries(TLineSeries)) do
  begin
    FunctionType:=TSmoothingFunction.Create(Self);
    with TSmoothingFunction(FunctionType) do
    begin
      Interpolate:=False;
      Factor:=6;
    end;
    DataSource:=Chart1[0];
  end;
end;
curvedLine.png
curvedLine.png (17.79 KiB) Viewed 17020 times

Re: TChart Line Chart Series with DrawStyle Curve Dips Below Zero - How to Fix?

Posted: Tue Sep 12, 2023 9:06 am
by 16894501
Thank you so much for the response.
Please note, that I am expecting the curve to pass through the points because it will affect the presented information.

Thank you
V

Re: TChart Line Chart Series with DrawStyle Curve Dips Below Zero - How to Fix?

Posted: Tue Sep 12, 2023 10:22 am
by yeray
Hello,

I've tried with a custom cubic spline, but it doesn't give the result I'd expect:
Project3_2023-09-12_12-21-08.png
Project3_2023-09-12_12-21-08.png (10.15 KiB) Viewed 16887 times

Code: Select all

uses Chart, Series;

var Chart1: TChart;

type TCoeficient = record
    X, Y, b, c, d: Double;
  end;

type TCoeficients = array of TCoeficient;
type TPoints = array of TPointFloat;

procedure CalculateCubicSplineCoefficients(const Points: TPoints; var Coefficients: TCoeficients);
var
  N, i: Integer;
  h: array of Double;
  alpha, l, mu, z: array of Double;
  c, b, d: array of Double;
begin
  N := Length(Points) - 1;

  SetLength(h, N);
  SetLength(alpha, N);
  SetLength(l, N);
  SetLength(mu, N);
  SetLength(z, N);
  SetLength(c, N);
  SetLength(b, N);
  SetLength(d, N);

  // Calculate h values
  for i := 0 to N - 1 do
    h[i] := Points[i + 1].X - Points[i].X;

  // Calculate alpha, l, mu, and z values
  for i := 1 to N - 1 do
  begin
    alpha[i] := (3.0 / h[i]) * (Points[i + 1].Y - Points[i].Y) - (3.0 / h[i - 1]) * (Points[i].Y - Points[i - 1].Y);
    l[i] := 2.0 * (Points[i + 1].X - Points[i - 1].X) - h[i - 1] * mu[i - 1];
    mu[i] := h[i] / l[i];
    z[i] := (alpha[i] - h[i - 1] * z[i - 1]) / l[i];
  end;

  // Calculate c, b, and d values
  for i := N - 1 downto 0 do
  begin
    c[i] := z[i] - mu[i] * c[i + 1];
    b[i] := (Points[i + 1].Y - Points[i].Y) / h[i] - h[i] * (c[i + 1] + 2.0 * c[i]) / 3.0;
    d[i] := (c[i + 1] - c[i]) / (3.0 * h[i]);
  end;

  // Store the coefficients
  SetLength(Coefficients, N);
  for i := 0 to N - 1 do
  begin
    Coefficients[i].X := Points[i].X;
    Coefficients[i].Y := Points[i].Y;
    Coefficients[i].c := c[i];
    Coefficients[i].b := b[i];
    Coefficients[i].d := d[i];
  end;
end;

procedure GenerateSmoothCurve(const Coefficients: TCoeficients; var SmoothCurve: TPoints);
var
  i, j: Integer;
  t: Double;
begin
  // Calculate the smooth points along the curve
  SetLength(SmoothCurve, 0);
  for i := 0 to High(Coefficients) - 1 do
  begin
    for j := 0 to 100 do // 100 points between each pair of input points
    begin
      t := j / 100;
      SetLength(SmoothCurve, Length(SmoothCurve) + 1);
      SmoothCurve[High(SmoothCurve)].X := Coefficients[i].X + t * (Coefficients[i + 1].X - Coefficients[i].X);
      SmoothCurve[High(SmoothCurve)].Y :=
        Coefficients[i].Y + t * (Coefficients[i + 1].Y + t * (Coefficients[i + 1].b + t * Coefficients[i + 1].c)) + t * t *
        (Coefficients[i + 1].d);
    end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var i: Integer;
    data: TPoints;
    coeficients: TCoeficients;
begin
  Chart1:=TChart.Create(Self);

  with Chart1 do
  begin
    Parent:=Self;
    Align:=alClient;
    Color:=clWhite;
    Gradient.Visible:=False;
    Walls.Back.Color:=clWhite;
    Walls.Back.Gradient.Visible:=False;
    Legend.Hide;
    View3D:=False;

    Axes.Left.SetMinMax(-1, 4);
    Axes.Left.Increment:=1;
    Axes.Bottom.Grid.Hide;
  end;

  with TPointSeries(Chart1.AddSeries(TPointSeries)) do
  begin
    Pointer.Size:=2;

    Add(0);
    Add(0);
    Add(3);
    Add(0);
    Add(0);
    Add(2);
    Add(2);
    Add(0);
    Add(1);
    Add(0);
    Add(0);
  end;

  // Calculate cubic spline
  SetLength(data, Chart1[0].Count);
  for i:=0 to Chart1[0].Count-1 do
  begin
    data[i].X:=Chart1[0].XValue[i];
    data[i].Y:=Chart1[0].YValue[i];
  end;

  CalculateCubicSplineCoefficients(data, coeficients);
  GenerateSmoothCurve(coeficients, data);

  with TLineSeries(Chart1.AddSeries(TLineSeries)) do
    for i:=0 to High(data) do
      AddXY(data[i].x, data[i].y);
end;
You could try to modify that algorithm or implement your own.