Recent

Author Topic: YGraphToImage returns negative value  (Read 1345 times)

kapibara

  • Hero Member
  • *****
  • Posts: 629
YGraphToImage returns negative value
« on: December 20, 2023, 09:00:44 am »
This demo is trying to move a caption and position it on the right pane on the chart.

Click three times on "Add Pane" in the attached project and you will notice one caption is out of view. For some reason the result of YGraphToImage becomes negative after the first pane which affects Caption.Top:

Code: Pascal  [Select][+][-]
  1. APane.Caption.Top:= Chart.YGraphToImage(APane.AutoScaleAxisTransform.MaxValue);

The labels of the right axis get more "fat" the more you click Add Pane, maybe thats a hint.

I got the same issue with YGraphToImage in the original code (ChartBox/ChartPane), so I made this small project to make it easier to find a solution.
« Last Edit: December 20, 2023, 09:06:37 am by kapibara »
Lazarus trunk / fpc 3.2.2 / Kubuntu 22.04 - 64 bit

kapibara

  • Hero Member
  • *****
  • Posts: 629
Re: YGraphToImage returns negative value
« Reply #1 on: December 20, 2023, 10:28:31 am »
Not that it solves the YGraphToImage result, but I had forgotten to multiply some of the HALF_GAP's by 2, so the panes where a tad too high. Fixed here.

Code: Pascal  [Select][+][-]
  1. procedure TfrmMain.ConfigureAxisTransformations(APane: TChartPane);
  2. var
  3.   i: Integer;
  4. begin
  5.   if Panes.Count = 1 then
  6.   begin
  7.     // A pane has already been added, i.e. Count is 1 for the first pane.
  8.     // The pane with index 0 does not contain a divider line.
  9.     APane.AutoScaleAxisTransform.MinValue := HALF_GAP;
  10.     APane.AutoScaleAxisTransform.MaxValue := APane.AutoScaleAxisTransform.MinValue
  11.                                            + DEFAULT_PANE_SIZE - (2* HALF_GAP);
  12. //    ShowMessage(APane.AutoScaleAxisTransform.MaxValue.ToString);
  13.     APane.Caption.Top:= Chart.YGraphToImage(APane.AutoScaleAxisTransform.MaxValue);
  14.  
  15.     Chart.Extent.YMax:= APane.AutoScaleAxisTransform.MaxValue;
  16.     Chart.Extent.YMin:= 0;
  17.     Chart.Extent.UseYMin:=True;
  18.     Chart.Extent.UseYMax:=True;
  19.  
  20.     InspectValues;
  21.     Exit;
  22.   end;
  23.  
  24.   // The pane has been added at the TOP of the pane stack. (Top of Chart)
  25.   if PaneIndex = Panes.Count -1 then
  26.   begin
  27.     APane.AutoScaleAxisTransform.MinValue:=
  28.       TChartPane(Panes[PaneIndex -1]).AutoScaleAxisTransform.MaxValue  + (2*HALF_GAP);
  29.  
  30.     APane.AutoScaleAxisTransform.MaxValue:= APane.AutoScaleAxisTransform.MinValue
  31.                                           + DEFAULT_PANE_SIZE - (2*HALF_GAP);
  32.  
  33.     APane.Caption.Top:= Chart.YGraphToImage(APane.AutoScaleAxisTransform.MaxValue);
  34.  
  35.     Chart.Extent.YMax:= APane.AutoScaleAxisTransform.MaxValue;
  36.     Chart.Extent.YMin:= 0;
  37.     Chart.Extent.UseYMin:=True;
  38.     Chart.Extent.UseYMax:=True;
  39.  
  40.     //Re-positions captions for all panes
  41.     for i:= 0 to panes.Count -1 do
  42.     begin
  43.       TChartPane(Panes[i]).Caption.Top:=
  44.         Chart.YGraphToImage(TChartPane(Panes[i]).AutoScaleAxisTransform.MaxValue);
  45.     end;
  46.  
  47.     InspectValues;
  48.   end;
  49. end;
  50.  
Lazarus trunk / fpc 3.2.2 / Kubuntu 22.04 - 64 bit

wp

  • Hero Member
  • *****
  • Posts: 12470
Re: YGraphToImage returns negative value
« Reply #2 on: December 20, 2023, 02:15:34 pm »
The problem is that the chart does all its calculations within the Draw method, the scaling factors and offsets needed by ImageToGraph are determined here. This means that when something affecting scaling, positioning etc. changes you must not call ImageToGraph before the chart has been repainted. Well... you probably ask yourself whether this is not happening, and in fact changing the Chart's Extent in your code does request a redraw by calling Invalidate, but this only puts it in the message queue of the OS, and the redraw is executed too late, after you have already positioned the Captions based on the wrong scaling coefficients.

Simply call Chart.Refresh before positioning the Captions (Refresh is executed immediately):
Code: Pascal  [Select][+][-]
  1.   if PaneIndex = Panes.Count -1 then
  2.   begin
  3.     APane.AutoScaleAxisTransform.MinValue:=
  4.       TChartPane(Panes[PaneIndex-1]).AutoScaleAxisTransform.MaxValue  + 2*HALF_GAP;
  5.  
  6.     APane.AutoScaleAxisTransform.MaxValue:= APane.AutoScaleAxisTransform.MinValue
  7.                                           + DEFAULT_PANE_SIZE - 2*HALF_GAP;
  8.     Chart.Extent.YMax:= APane.AutoScaleAxisTransform.MaxValue;
  9.     Chart.Extent.YMin:= 0;
  10.     Chart.Extent.UseYMin:=True;
  11.     Chart.Extent.UseYMax:=True;
  12.  
  13.     Chart.Refresh;   // <--- ADDED
  14.  
  15.     //Re-positions captions for all panes
  16.     for i:= 0 to panes.Count -1 do
  17.     begin
  18.       TChartPane(Panes[i]).Caption.Top:=
  19.         Chart.YGraphToImage(TChartPane(Panes[i]).AutoScaleAxisTransform.MaxValue);
  20.     end;

I removed the call to place the Caption of the new pane because ALL captions are handled at the end.

And for completeness: You code has lots of memory leaks, but you probably know this and skipped it for simplicity of the demo.

kapibara

  • Hero Member
  • *****
  • Posts: 629
Re: YGraphToImage returns negative value
« Reply #3 on: December 20, 2023, 05:31:41 pm »
Ah, I understand. Would never have found that out myself :-)

For some reason Refresh didn't have effect. But Application.ProcessMessages did. Could it be a bug in Refresh for Linux? You run Windows, if I remember right.

Just for fun I moved the positioning code to Chart.AfterDraw and from there it works without anything else. But then Captions are re-positioned also when they don't need to be, so I'll use ProcessMessages, or Refresh if that can be made to work.

About leaks, yes this was just a test. Axis, Caption, Transformations and AutoscaleTransformation should be free'd if a pane is removed. Other than that, there is no leak that I'm aware of. :)
« Last Edit: December 20, 2023, 05:56:33 pm by kapibara »
Lazarus trunk / fpc 3.2.2 / Kubuntu 22.04 - 64 bit

wp

  • Hero Member
  • *****
  • Posts: 12470
Re: YGraphToImage returns negative value
« Reply #4 on: December 20, 2023, 06:45:50 pm »
For some reason Refresh didn't have effect. But Application.ProcessMessages did. Could it be a bug in Refresh for Linux?
I don't know...

Just for fun I moved the positioning code to Chart.AfterDraw and from there it works without anything else. But then Captions are re-positioned also when they don't need to be.
Yes, that's a good idea. You could introduce a state variable (e.g., FCaptionPositionPending) which you set to true in your current code and which you evaluate in AfterDraw. When it is true you set the positions; and at the end you reset the state variable back to false so that AfterDraw is not executed in the next Paint cycle.

 

TinyPortal © 2005-2018