Recent

Author Topic: TAChart how to make different width and/or color only for a specific grid line  (Read 2616 times)

tk

  • Sr. Member
  • ****
  • Posts: 372
Hello, I need to highlight the horizontal zero-value grid line in my chart, i.e., display it in a different color and with a thicker line width.
It can be seen here https://wiki.freepascal.org/TAChart on the screenshot below.
Both zero value grid lines are drawn with different style.
How to do this programmatically in TAChart?

Further information is little bit off topic but maybe it can be useful:

In my programs, I also use the Chart.JS JavaScript library.
I found a good video demonstrating this in Chart.JS: https://www.youtube.com/watch?v=wijUFALw3tQ
This works as expected.

I also use Steema TeeChart Delphi VCL library (Standard Edition without source code).
I don't know how to make this there as well.
Maybe someone knows how to do the same thing in Steema TeeChart?
The official forum https://www.steema.com/support/ seems to be closed to new registrations, so I can't ask questions there :(

Thanks


wp

  • Hero Member
  • *****
  • Posts: 13349
In TAChart you add a TConstantLine series to the chart. For a y=0 line, make sure that it has LineStyle = lsHorizontal and Position = 0.  A x=0 line needs LineStyle = lsVertical. Use the Pen property of the series to modify color and style. And you may want to uncheck ShowInLegend to avoid having these auxiliary lines in the legend.

I cannot find something like this in TeeChart. But you can always add a dummy line series running along the zero-value grid line.

The official forum https://www.steema.com/support/ seems to be closed to new registrations, so I can't ask questions there :(
Yet another reason to switch to TAChart...

tk

  • Sr. Member
  • ****
  • Posts: 372
Thank you for such a quick answer!

In TAChart you add a TConstantLine series to the chart.

One more question:
Will this constant line series count towards the automatic vertical range calculation (when LeftAxis.Automatic := True)?
Because the zero line is not always visible in my chart - the grid lines shown depend on automatic range calculation.

I cannot find something like this in TeeChart. But you can always add a dummy line series running along the zero-value grid line.

I already tried to use the dummy line series in both TaChart and Teechart but they always count towards the automatic vertical range calculation.
They cannot be excluded from that calculation (or I don't know how they can be excluded).
Of course I could compute the range manually from 'working' series to prevent this but I don't want to do this.
I prefer to use the automatic calculation with default rounding and display settings.
« Last Edit: July 17, 2025, 01:09:14 pm by tk »

wp

  • Hero Member
  • *****
  • Posts: 13349
I see. Yes - the ConstantLine series is considered when the overall axis range is calculated.

The following code seems to work:
  • In the OnExtentChanging event of the chart hide the ConstantLine series.
  • In the OnExtentChanged event of the chart, use the chart's LogicalExtent (the range of the x and y axes, since ConstantLine series is hidden this is without the constant line) to decide whether the ConstantLine should be turned on again:
Code: Pascal  [Select][+][-]
  1. uses
  2.   TAChartUtils;
  3.  
  4. procedure TForm1.Chart1ExtentChanging(ASender: TChart);
  5. begin
  6.   Chart1ConstantLine1.Active := false;
  7. end;  
  8.  
  9. procedure TForm1.Chart1ExtentChanged(ASender: TChart);
  10. var
  11.   ext: TDoubleRect;
  12. begin
  13.   ext := Chart1.LogicalExtent;
  14.   Chart1ConstantLine1.Active := ext.b.y * ext.a.y < 0;
  15. end;
  16.  

Another possibility would be to skip the idea with the ConstantLine series and to hook into one of the chart's drawing events. OnAfterDrawBackwall or OnAfterCustomDrawBackwall seem to be good candidates. Get the logical extent in a local variable and replace the y value by 0. Calculate the corresponding screen points and draw the connecting line:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Chart1AfterCustomDrawBackWall(ASender: TChart;
  2.   ADrawer: IChartDrawer; const ARect: TRect);
  3. var
  4.   ext: TDoubleRect;
  5.   P1, P2: TPoint;
  6. begin
  7.   ext := ASender.LogicalExtent;
  8.   ext.a.y := 0;
  9.   ext.b.y := 0;
  10.   P1 := ASender.GraphToImage(ext.a);
  11.   P2 := ASender.GraphToImage(ext.b);
  12.   ADrawer.SetPenParams(psSolid, clYellow, 3);
  13.   ADrawer.Line(P1, P2);
  14. end;              

The problem here is that the axis grid is drawn after the background and the grid line is painted over this manually drawn zero-line. Only when the zero line has the same color as the grid lines, this does not matter...
« Last Edit: July 17, 2025, 03:35:15 pm by wp »

wp

  • Hero Member
  • *****
  • Posts: 13349
Ah - there's a better solution:
  • In the object tree, click on the AxisList of the chart, then right-click and "Add item". This adds a third axis to the chart
  • If you want a horizontal zero line, set the Alignment of the new axis to alBottom, otherwise alLeft.
  • Turn this axis line ON: (new axis).AxisPen.Visible := true; you may also want to change linewidth and color here
  • Turn off the grid of the new axis: (new axis).Grid.Visible := false
  • Maybe you want to turn OFF the axis labels and ticks of the new axis: (new axis).Marks.Visible := false.
  • Move the new axis line to the zero position: (new axis).Position := 0 and (new axis).PositionUnits := cuGraph; this interprets the "0" in the unit system of the data points.
  • Move the new axis above the old axis (because otherwise the grid will be painted over it in case of coincidence): (new axis).ZPosition := 1. But now the new axis will also be before the series which have ZPosition := 0 by default. If you don't like this, you must also set the ZPosition of each series to a larger value than the ZPosition of the new axis. (Or: apply all the changes to the old axis and keep the default settings of the new axis.)
I hope I did not forget anything...
« Last Edit: July 17, 2025, 04:02:02 pm by wp »

tk

  • Sr. Member
  • ****
  • Posts: 372
Ah - there's a better solution:

Thanks I'll try this solution later when someone has problem with my own solution, which I made in the meantime.

My solution is now:
a) After each chart data update calculate Minimum and Maximum values from all 'working' line series.
b) Then show dummy zero line series (in my case the first series) only when zero fits between minimum and maximum:
Code: Pascal  [Select][+][-]
  1.   Chart.Series[0].Active := (Minimum <= 0) and (Maximum >= 0);

This is not clean solution, the dummy zero line is not visible in certain situations when it actually should be (this depends on vertical range rounding algorithms used in the automatic range calculation in each chart component), but for now it is a good tradeoff for me.
And this works both for TAChart and TeeChart.

tk

  • Sr. Member
  • ****
  • Posts: 372
Finally I've used this wp's solution:

Another possibility would be to skip the idea with the ConstantLine series and to hook into one of the chart's drawing events. OnAfterDrawBackwall or OnAfterCustomDrawBackwall seem to be good candidates. Get the logical extent in a local variable and replace the y value by 0. Calculate the corresponding screen points and draw the connecting line.

Because it is simple (does not need any extra series or axes) and virtually the same way can be made in TeeChart.

I use a slightly modified solution.
For more details see my answer here:
https://stackoverflow.com/questions/79704456/steema-teechart-how-to-make-different-width-and-color-only-for-a-specific-horizo

EDIT:
Just to be safe, I'm posting my answer here because in the Stack Overflow ecosystem you never know who will edit your answer and how.

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3. //...
  4.   Chart1:=TChart.Create(Self);
  5. //...
  6.   Chart1.OnAfterCustomDrawBackWall := ChartAfterCustomDrawBackWall;
  7. end;
  8.  
  9. procedure TForm1.ChartAfterCustomDrawBackWall(Sender: TChart; ADrawer: IChartDrawer; const ARect: TRect);
  10. var
  11.   x0Pos, x1Pos, y0Pos, y1Pos, yPos: Integer;
  12. begin
  13.   yPos := Sender.YGraphToImage(0);
  14.   y0Pos := Sender.YGraphToImage(Sender.CurrentExtent.b.Y);
  15.   y1Pos := Sender.YGraphToImage(Sender.CurrentExtent.a.Y);
  16.   x0Pos := Sender.XGraphToImage(Sender.CurrentExtent.a.X);
  17.   x1Pos := Sender.XGraphToImage(Sender.CurrentExtent.b.X);
  18.   if (yPos >= y0Pos) and (yPos <= y1Pos) then
  19.   begin
  20.     ADrawer.SetPenColor(clBlack);
  21.     ADrawer.SetPenWidth(1);
  22.     ADrawer.Line(x0Pos, YPos, x1Pos, YPos);
  23.   end;
  24. end;
  25.  
« Last Edit: July 24, 2025, 10:07:34 am by tk »

 

TinyPortal © 2005-2018