Lazarus

Programming => Graphics and Multimedia => TAChart => Topic started by: Muso on July 27, 2022, 08:52:27 pm

Title: how to bring marks to front
Post by: Muso on July 27, 2022, 08:52:27 pm
I often have the case that a mark to a line series is covered by other series, see the attached screenshot.

Is there a way to force that notes are painted topmost/as last part when the chart is painted?
Title: Re: how to bring marks to front
Post by: wp on July 27, 2022, 10:37:50 pm
The marks are drawn along with the series. Therefore, the marks of the series drawn first may be overwritten by the series drawn later. But the drawing order of the series is determined by their ZPosition. So, if you want to fully see the marks of one particular series you simply must exchange the ZPosition of that series with the ZPosition of the top-most series. The only pre-requisite is that the series have a ZPosition different from the default 0.

You can even merge this into the OnPointClick handler of the TDatapointMarksClickTool (see the related sample code in the other thread; in the object inspector, set the ZPosition of the two series to 1 and 2, respectively):

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ChartToolset1DataPointMarksClickTool1PointClick(
  2.   ATool: TChartTool; APoint: TPoint);
  3. var
  4.   tool: TDatapointMarksClickTool;
  5.   ser1: TBasicChartSeries;
  6.   ser2: TBasicChartSeries;
  7.   i: Integer;
  8.   z1, z2: Integer;
  9. begin
  10.   tool := ATool as TDatapointMarksClickTool;
  11.  
  12.   ser1 := tool.Series;
  13.   z1 := ser1.ZPosition;
  14.   z2 := -1;
  15.   for i := 0 to Chart1.SeriesCount-1 do
  16.     if (Chart1.Series[i] is TChartSeries) and
  17.        (Chart1.Series[i].ZPosition > z2) then
  18.     begin
  19.       ser2 := Chart1.Series[i];
  20.       z2 := ser2.ZPosition;
  21.     end;
  22.   if ser1 <> ser2 then
  23.   begin
  24.     ser1.ZPosition := z2;
  25.     ser2.ZPosition := z1;
  26.   end;
  27.  
  28.   ShowMessage(Format('Series "%s" datapoint #%d clicked.', [(ser1 as TChartSeries).Title, tool.PointIndex]));
  29. end;
Title: Re: how to bring marks to front
Post by: Muso on July 27, 2022, 10:43:37 pm
The marks are drawn along with the series. Therefore, the marks of the series drawn first may be overwritten by the series drawn later. But the drawing order of the series is determined by their ZPosition.
Thanks.
This is what I already tried, but the users want to see all marks by one view (no need to click to see them). In my example there are marks in the red and the blue series. Therefore changing the z-order does not solve the problem, since some notes will not be properly visible.

Therefore I thought that when the series are drawn first and then subsequently the marks, the issue should be solvable. If this is yet possible, this might be nice new feature  ;) .
Title: Re: how to bring marks to front
Post by: Muso on July 27, 2022, 11:28:14 pm
You can even merge this into the OnPointClick handler of the TDatapointMarksClickTool

I gave this a try but get a crash when I try to access the Z-order see attached screenshot.
Is this a bug in the tool or do I have to do something special?
Title: Re: how to bring marks to front
Post by: wp on July 27, 2022, 11:41:30 pm
This is what I already tried, but the users want to see all marks by one view (no need to click to see them). In my example there are marks in the red and the blue series. Therefore changing the z-order does not solve the problem, since some notes will not be properly visible.
But there are other tools. The one which does not require a mouse press is the DatapointHintTool. Set its UseDefaultHintText to false to avoid a hint popping up. Then add the Z-reordering code into the OnHint event. This moves the series over which the mouse hovers to the top of the Z stack. Since in your case there are really lots of data points it should be almost impossible to miss a data point. If it is nevertheless, you should add the nptCustom option to the series' ToolTargets. This fires the OnHint event also when the mouse is over the interpolating line.

Therefore I thought that when the series are drawn first and then subsequently the marks, the issue should be solvable. If this is yet possible, this might be nice new feature  ;) .
This would mean that every series must be painted twice, once with its normal representation as lines, symbols, bars or whatever, and a second time with its labels only. This would double painting time, and charts almost completely covered by data points like in your case would draw even slower.
Title: Re: how to bring marks to front
Post by: wp on July 27, 2022, 11:47:35 pm
I gave this a try but get a crash when I try to access the Z-order see attached screenshot.
Is this a bug in the tool or do I have to do something special?
I don't know. Of course I tested the idea, and it is working for my sample project - see attachment (which contains the Z-Order code in both DatapointMarkClick- and DatapointHintTools, i.e. reacts on moving the mouse over the data points/connecting lines and on clicking on the mark labels).
Title: Re: how to bring marks to front
Post by: Muso on July 28, 2022, 07:34:18 pm
I don't know. Of course I tested the idea, and it is working for my sample project

OK, my mistake, I used your code inside of

Code: Pascal  [Select][+][-]
  1. DataPointMarksClickToolBeforeMouseDown(ATool:...
But since I use the tool as
Code: Pascal  [Select][+][-]
  1. tool:= ATool as TDatapointMarksClickTool;
this cannot work.

However, I fixed this and implemented the z.order change code to a DataPintHintTool.

I debugged ans see that all my TLineSeries have the ZPosition 0.

I thought, OK, I set the current series to 0, the other visible ones to 1. But this made the other ones painted on top. Instead I have to paint my series in z-order 1, the others at 0.
Here is my final solution:
Code: Pascal  [Select][+][-]
  1. var
  2.  currentSeries, otherSeries : TBasicChartSeries;
  3.  i : integer;
  4. begin
  5.  otherSeries:= nil;
  6.  currentSeries:= ATool.Series;
  7.  
  8.  for i:= 0 to MainForm.SIXCH.SeriesCount - 1 do
  9.  begin
  10.   if (MainForm.SIXCH.Series[i] is TLineSeries)
  11.    and (MainForm.SIXCH.Series[i].Active)
  12.    then
  13.   begin
  14.    otherSeries:= MainForm.SIXCH.Series[i];
  15.    if currentSeries <> otherSeries then
  16.     otherSeries.ZPosition:= 0;
  17.   end;
  18.  end;
  19.  // repaint chart if necessary
  20.  if currentSeries.ZPosition < 1 then
  21.  begin
  22.   currentSeries.ZPosition:= 1;
  23.   MainForm.SIXCH.Invalidate;
  24.  end;

So there is a bug in the description of ZPosition, because it says, that to bring a series to front one should set it to 0.
Title: Re: how to bring marks to front
Post by: wp on July 28, 2022, 11:24:47 pm
So there is a bug in the description of ZPosition, because it says, that to bring a series to front one should set it to 0.
Where is that? I did not find such a statement in https://wiki.lazarus.freepascal.org/TAChart_documentation
Title: Re: how to bring marks to front
Post by: Muso on July 29, 2022, 02:26:55 pm
Where is that? I did not find such a statement in https://wiki.lazarus.freepascal.org/TAChart_documentation (https://wiki.lazarus.freepascal.org/TAChart_documentation)
In the tooltip, see attached.
------------------------
Again many thanks for the marks click tool!

As final feedback:
- I miss a hover event. When the users moves the mouse over the mark area, the cursor should be changeable and it should also be possible to show then a tooltip with custom content.

- marks should have the feature to display one e.g. the first 3 words of its content, the rest as tooltip, as I described.
TinyPortal © 2005-2018